diff --git a/.gitignore b/.gitignore index fe17ed5..d001440 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ tags doc/ log_subdir/ +coverage/ diff --git a/Doxyfile b/Doxyfile index dab44b8..2f3d864 100644 --- a/Doxyfile +++ b/Doxyfile @@ -655,7 +655,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = src +INPUT = include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff --git a/SConstruct b/SConstruct index 695afbe..7b7f1c4 100644 --- a/SConstruct +++ b/SConstruct @@ -36,6 +36,12 @@ AddOption('--test_xml', help='Create a result.xml when running the testsuite' ) +AddOption('--coverage', + dest='coverage', + action='store_true', + help='Build with coverage (gcov) support' +) + AddOption('--stack-protection', dest = 'stack_protection', action='store_true', @@ -59,6 +65,11 @@ def InstallPerm(env, dest, files, perm): env = Environment(tools = ['default', 'textfile']) + +#allow shell environment to override compiler +if os.environ.has_key('CC'): + env['CC'] = os.environ['CC'] + env.AddMethod(InstallPerm) Export('env', 'build_type', 'prefix', 'version_info', 'test_xml') @@ -80,6 +91,9 @@ if GetOption('stack_protection'): env.Append(CFLAGS = ['-fstack-protector', '--param=ssp-buffer-size=4']) env.Append(CPPDEFINES = ['_FORTIFY_SOURCE=2']) +if GetOption('coverage'): + env.Append(CFLAGS = ['--coverage']) + env.Append(LINKFLAGS = ['--coverage']) #put all .sconsign files in one place env.SConsignFile() diff --git a/gencoverage.sh b/gencoverage.sh new file mode 100755 index 0000000..bc73696 --- /dev/null +++ b/gencoverage.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +scons --coverage test +mkdir -p coverage && cd coverage +lcov -c -d ../build/src/ -o libucore.info +genhtml libucore.info + +echo "Test report available at file:///$(pwd)/index.html" diff --git a/include/ucore/atomic.h b/include/ucore/atomic.h index 5311352..a3b4a67 100644 --- a/include/ucore/atomic.h +++ b/include/ucore/atomic.h @@ -30,7 +30,7 @@ * Atomically increment *ptr * @return *ptr before the increment */ -#define UC_ATOMIC_INC(ptr) UC_ATOMIC_SUB((ptr), 1) +#define UC_ATOMIC_INC(ptr) UC_ATOMIC_ADD((ptr), 1) /** diff --git a/include/ucore/clock.h b/include/ucore/clock.h index b64c4a9..7014c63 100644 --- a/include/ucore/clock.h +++ b/include/ucore/clock.h @@ -3,7 +3,10 @@ #include #include - +/** @addtogroup Timers + * @{ + * Clock abstractions + */ #ifdef __cplusplus extern "C" { #endif @@ -117,5 +120,6 @@ void uc_settable_clock_set(struct UCoreSettableClock *uclock, } #endif +/** @} (addtogroup) */ #endif diff --git a/include/ucore/dbuf.h b/include/ucore/dbuf.h new file mode 100644 index 0000000..c3c841c --- /dev/null +++ b/include/ucore/dbuf.h @@ -0,0 +1,127 @@ +#ifndef dbuf_H_ +#define dbuf_H_ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * dbuf is a generic buffer that will grow automatically. + * call buf_ensure() to reserve space, and don't write more data + * than you told buf_ensure() to reserve. + * + * You write data to buf->end + * When finished writing data + * you call dbuf_added/( with the no. of bytes you've added. + * + * You read data from buf->start, the buffer has dbuf_len() bytes of data + * When you've read and processed the data, call dbuf_take() + * to indicate you're done with the data. + * + * typically(but add error checking) + * @code + * DBuf buf; + * dbuf_init(&buf,4096); + * for(;;) { + * dbuf_ensure(buf,4096); + * size_t len = fread(buf,4096,1,f); + * dbuf_added(&buf,len); + * char *end; + * while((end = memchr(buf.start,'\n',dbuf_len(&dbuf))) != NULL) { //look for a newline + * ++end; + * int linelen = end - buf.start; + * process_line(buf.start,linelen); + * dbuf_take(&dbuf,linelen); + * } + * } + * @endcode + */ +typedef struct DBuf DBuf; +struct DBuf { + /** Start of user data */ + unsigned char *start; + /** End of user data */ + unsigned char *end; + /** Start of underlying buffer. Internal use.*/ + unsigned char *bufstart; + /** End of underlying buffer. Internal use.*/ + unsigned char *bufend; +}; +/** Free the internals of a DBuf, does not free buf itself. + * Does nothing if buf == NULL + * + * @param buf buffer to free + */ +void uc_dbuf_free(DBuf *buf); + +/** Initialize a dbuf. +* +* @param buf buffer to initialize +* @param initlen inital capacity of the buffer +* @return 0 on success +*/ +int uc_dbuf_init(DBuf *buf,size_t initlen); + +/** Get length of user data in the buffer + * + * @param buf buffer to get length of + * @return length of the user data + */ +size_t uc_dbuf_len(DBuf *buf); + +/** Allocated size of the buffer + * + * @param buf buffer to get allocated length of + * @return allocated length of the underlying buffer + */ +size_t uc_dbuf_buflen(DBuf *buf); + +/** Get remaining space in the buffer + * + * @param buf buffer + * @return remaining space (after buf->end) in the buffer + */ +size_t uc_dbuf_remaining(DBuf *buf); + +/** ensure the buffer can hold 'capacity' no of bytes, + * + * @param buf buffer + * @param capacity ensure there is room for capacity bytes after buf->end + * @return 0 on success + */ +int uc_dbuf_ensure(DBuf *buf,size_t capacity); + +/** Trim the buffer to hold no more space than needed. + * + * @param buf buffer to trum + */ +void uc_dbuf_trim(DBuf *buf); + +/** Indicate you've taken 'len' bytes out of the buffer. + * Advances buf->start by len bytes + * This may re-set buf->start and buf->end if e.g. all data + * is taken out of the buffer. + * + * @param buf buffer + * @param len length that was taken out of the buffer + * @return 0 on success. Non-zero if len is larger than the bufer capacity + */ +int uc_dbuf_take(DBuf *buf,size_t len); + +/** Indicate you've added 'len' bytes to the buffer + * Advances buf->end by len bytes + * User must always make sure (e.g. by using uc_dbuf_ensure) + * that the buffer has the capacity to add @len bytes + * + * @param buf buffer + * @param len length that was added to the buffer. + */ +void uc_dbuf_added(DBuf *buf,size_t len); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/ucore/fd_utils.h b/include/ucore/fd_utils.h index c2145bc..9c8d178 100644 --- a/include/ucore/fd_utils.h +++ b/include/ucore/fd_utils.h @@ -10,42 +10,66 @@ extern "C" { * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_nonblocking(int fd); +int uc_set_nonblocking(int fd); /** * set the file descriptor to blocking * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_blocking(int fd); +int uc_set_blocking(int fd); /** * set the socket file descriptor to SO_REUSEADDR * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_reuseaddr(int sockfd); +int uc_set_reuseaddr(int sockfd); /** * enable TCP keepalive on the socket * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_tcp_keepalive(int sockfd); +int uc_set_tcp_keepalive(int sockfd); + +struct UCKeepConfig { + /** The maximum number of keepalive probes TCP should + * send before dropping the connection. (TCP_KEEPCNT socket option) + */ + int keepcnt; + /** The time (in seconds) the connection needs to remain + * idle before TCP starts sending keepalive probes (TCP_KEEPIDLE socket option) + */ + int keepidle; + /** The time (in seconds) between individual keepalive probes. + * (TCP_KEEPINTVL socket option) + */ + int keepintvl; +}; + +/** Set the keepalive options on the socket +* This also enables TCP keepalive on the socket +* +* @param fd file descriptor +* @param fd file descriptor +* @return 0 on success -1 on failure +*/ +int uc_set_tcp_keepalive_cfg(int sockfd, const struct UCKeepConfig *cfg); /** * Set the socket send timeout, in miliseconds * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_send_timeout(int sockfd,long timeoutms); +int uc_set_send_timeout(int sockfd,long timeoutms); /** * Set the socket receive timeout, in miliseconds * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_receive_timeout(int sockfd,long timeoutms); +int uc_set_receive_timeout(int sockfd,long timeoutms); /* * Set the IP DSCP (differnetiated services code point) header value @@ -53,7 +77,7 @@ int set_receive_timeout(int sockfd,long timeoutms); * @param fd file descriptor * @return 0 on success -1 on failure */ -int set_ip_dscp(int fd, unsigned int dscp); +int uc_set_ip_dscp(int fd, unsigned int dscp); #ifdef __cplusplus diff --git a/include/ucore/iomux.h b/include/ucore/iomux.h index 7f8c2f7..78e4ccd 100644 --- a/include/ucore/iomux.h +++ b/include/ucore/iomux.h @@ -2,7 +2,18 @@ #define IO_MUX_H_ #include "timers.h" - +/** @addtogroup IOMux + * @{ + * The IOMux is an I/O multiplexer, implementing a main eventloop. + * The event loop can be used to register callbacks for activity on + * file descriptors such as sockets and pips, and for timers. + * + * The IOMux is backed either by the select() system call, or the epoll + * facility on Linux. Several IOMuxes can run in different threads concurrently, + * but note that the IOMux itself is not thread safe so operations in one thread + * cannot work on an IOMux running in another thread. + * + * */ #ifdef __cplusplus extern "C" { #endif @@ -22,9 +33,20 @@ enum IOMUX_TYPE { IOMUX_TYPE_EPOLL }; -/** Opaque struct */ +/** The IOMux used to issue mux operations. This is to be treated as an opaque + * handle. Use iomux_create() or iomux_create_ex() to create an IOMux, register + * callbakcs, and call iomux_run() to process the events + */ struct IOMux; + struct IOMuxFD; + +/** Callback function used in struct IOMuxFD + * @param mux the mux that issued the callback. + * @param fd the file desciptor structure. + * @param event A bitmask of the MUX_EV_XX values. Both a read and a write event + * might be signalled in the same callback + */ typedef void (*iomux_cb)(struct IOMux *mux, struct IOMuxFD *fd, unsigned int event); /** Represents a watched file descriptor */ @@ -43,6 +65,9 @@ struct IOMuxFD { }; /** Creates a new IOMux. + * + * @param type the underlying implementation to use @see IOMUX_TYPE + * @return Newly allocated IOMux. NULL if memory allocation fails. */ struct IOMux *iomux_create(enum IOMUX_TYPE type); @@ -58,19 +83,30 @@ struct IOMux *iomux_create_ex(enum IOMUX_TYPE type, struct UCoreClock *uclock); void iomux_delete(struct IOMux *mux); /** Runs the event loop of the mux - * returns if the number of file descriptors and timers are 0 or + * + * @return if the number of file descriptors and timers are 0 or * an error occurs */ int iomux_run(struct IOMux *mux); -/* Adds a file descriptor to the watch set, - * returns 0 on success, an errno value on error */ +/* Adds a file descriptor to the watch set. + * The file descriptor integer value must reside in only + * one struct IOMuxFD. Adding e.g. the same file descriptor + * in 2 different struct IOMuxFD, one for read events and one for + * write events is not supported. + * + * @return 0 on success, an errno value on error + */ int iomux_register_fd(struct IOMux *mux, struct IOMuxFD *fd); -/** Removes the file descriptor*/ +/** Removes the file descriptor + * + * @return 0 on success, an errno value on error + */ int iomux_unregister_fd(struct IOMux *mux, struct IOMuxFD *fd); /** Updates the events to watch for in fd->what - * returns 0 on success, an errno value on error */ + * @return 0 on success, an errno value on error + */ int iomux_update_events(struct IOMux *mux, struct IOMuxFD *fd); @@ -100,7 +136,7 @@ int iomux_update_events(struct IOMux *mux, struct IOMuxFD *fd); iomux_update_events((mux), (fd));\ }) -/** Returns a UCTimer instance tied to this mux. +/** @return a UCTimer instance tied to this mux. * Used to schedule timers within this mux. */ struct UCTimers *iomux_get_timers(struct IOMux *mux); @@ -109,5 +145,6 @@ struct UCTimers *iomux_get_timers(struct IOMux *mux); } #endif +/** @} (addtogroup)*/ #endif diff --git a/include/ucore/mbuf.h b/include/ucore/mbuf.h new file mode 100644 index 0000000..406f951 --- /dev/null +++ b/include/ucore/mbuf.h @@ -0,0 +1,75 @@ +#ifndef UC_MBUF_H_ +#define UC_MBUF_H_ +#include +#include + +#ifdef __GNUC__ +# define UC_INLINE inline __attribute__((always_inline)) +#else +# define UC_INLINE inline +#endif + +enum UC_MBUF_FLAGS { + UC_MBUF_FL_MALLOC = 0x0001, //MBuf and MBuf->bufstart are seperatly malloc'd + UC_MBUF_FL_MALLOC_BUF = 0x0001, //MBuf is not malloc'd. MBuf->bufstart is malloc'd + UC_MBUF_FL_MALLOC_INLINE = 0x0002, //MBuf is one big malloc. Can't be resized + UC_MBUF_FL_STATIC = 0x0004, //MBuf and MBuf->bustart is user controlled. Can't be resized +}; + +struct MBuf { + struct MBuf *next; + uint8_t *p1; + uint8_t *p2; + uint8_t *p3; + uint8_t *p4; + void *cookie; + uint32_t flags; + uint32_t allocated; + uint8_t *bufstart; + uint8_t *head; + uint8_t *tail; + uint8_t inline_data[0]; +}; + +/* Conventions: + * push - prepend data to the buffer (in the head room part) + * pull - remove data from the beginning of the message. + * put - Add data to the buffer. + */ +struct MBuf *uc_mbuf_new_hr(uint32_t len, uint32_t headroom); +#define uc_mbuf_new(len) uc_mbuf_new_hr((len), 0) + +struct MBuf *uc_mbuf_new_inline_hr(uint32_t len, uint32_t headroom); +#define uc_mbuf_new_inline(len) uc_mbuf_new_inline_hr((len), 0) + +void uc_mbuf_free(struct MBuf *mbuf); +struct MBuf *uc_mbuf_copy_data(struct MBuf *mbuf); + +void uc_mbuf_init(struct MBuf *mbuf, uint8_t *buf, uint32_t len, uint32_t headroom, uint32_t flags); +void uc_mbuf_zero(struct MBuf *mbuf); +void uc_mbuf_reset(struct MBuf *mbuf); + +static UC_INLINE uint32_t uc_mbuf_len(struct MBuf *mbuf) +{ + return (uint32_t)(mbuf->tail - mbuf->head); +} + +static UC_INLINE uint32_t uc_mbuf_tailroom(struct MBuf *mbuf) +{ + return mbuf->allocated - (uint32_t)(mbuf->tail - mbuf->bufstart); +} + +static inline uint32_t uc_mbuf_headroom(struct MBuf *mbuf) +{ + return (uint32_t)(mbuf->head - mbuf->bufstart); +} + +uint8_t *uc_mbuf_put(struct MBuf *mbuf, uint32_t size); + +uint8_t *uc_mbuf_push(struct MBuf *mbuf, uint32_t size); + +uint8_t *uc_mbuf_pull(struct MBuf *mbuf, uint32_t size); + +#undef UC_INLINE + +#endif diff --git a/include/ucore/saturating_math.h b/include/ucore/saturating_math.h index f870222..e10539d 100644 --- a/include/ucore/saturating_math.h +++ b/include/ucore/saturating_math.h @@ -1,8 +1,11 @@ #include -/** Implementation of saturated operation on uintaa_t. - * Opaerations are clamped betweehn 0 and UINTaa_MAa instead of - * wrapping around*/ +/** Implementation of saturated operation on uintxx_t. + * + * Operations are clamped between 0 and UINTxx_MAX instead of + * wrapping around. + * + * multiplication, subtraction and addition operations are provided.*/ #define UC_SAT_ADD(a, b, type, max_val)\ ((type)((a) + (b)) >= (a) ? (a) + (b) : (max_val)) diff --git a/include/ucore/string.h b/include/ucore/string.h index 56eaf73..9b09103 100644 --- a/include/ucore/string.h +++ b/include/ucore/string.h @@ -9,10 +9,6 @@ extern "C" { #endif -size_t -uc_base64_enc(const unsigned char *data,size_t len,unsigned char *result); -size_t -uc_base64_dec(const unsigned char *data, size_t len, unsigned char *result); char* uc_hex_encode(const uint8_t *in, size_t len, char *out); diff --git a/include/ucore/strvec.h b/include/ucore/strvec.h new file mode 100644 index 0000000..3789d78 --- /dev/null +++ b/include/ucore/strvec.h @@ -0,0 +1,90 @@ +#include + +#ifndef UC_STRVEC_H_ +#define UC_STRVEC_H_ + +#ifdef __cplusplus +extern "C" { +#endif +/** A Vector of strings.*/ +struct UCStrv +{ + /** number of strings */ + size_t cnt; + /** allocated room in strings (internal use) */ + size_t allocated; + /** Array of strings */ + char **strings; +}; + +/** Initialize a UCStrv + * + * @param strv + */ +void uc_strv_init(struct UCStrv *strv); + +/** + * Grow the UCStrv so it can hold n more items + * + * @param strv UCStrv to grow + * @param cnt expand strvec to hold this cnt more items + * @return 0 on success, otherwise failure + **/ +int uc_strv_grow(struct UCStrv *strv, size_t cnt); + +/** + * Expand the UCStrv to hold 1 more item + * + * @param strv UCStrv to expand + * @return 0 on success, otherwise failure + **/ +int uc_strv_expand(struct UCStrv *strv); + +/** + * Append a string to the UCStrv, the string will be copied in + * + * @param strv UCStrv to append to + * @param str String to append, the string will be copied. + * @return 0 on success, otherwise failure + **/ +int uc_strv_append(struct UCStrv *strv, const char *str); + +/** + * Append a string to the strvec, nocopy variang. + * The string shold be dynamically allocated, as + * destroying the UCStrv will free() all items. + * + * @param strv UCStrv to add to + * @param str the string to append + * @return 0 on success, otherwise failure + **/ +int uc_strv_append_nocopy(struct UCStrv *strv, char *str); + +/** + * Append a NULL pointer to the UCStrv, (cnt is not increased, + * so another _appended item will overwrite this NULL terminator) + * + * @param strv UCStrv to append to + * @return 0 on success, otherwise failure + **/ +int uc_strv_null_terminate(struct UCStrv *strv); + +/** + * free all strings in this UCStrv, cnt will be 0 after this + * @param strv UCStrv to clear + */ +void uc_strv_clear(struct UCStrv *strv); + +/** + * free all elements as well as the underlying array, the UCStrv is + * unusable after this call + * + * @param strv UCStrv to destroy + */ +void uc_strv_destroy(struct UCStrv *strv); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ucore/supervisor.h b/include/ucore/supervisor.h new file mode 100644 index 0000000..b33aa2b --- /dev/null +++ b/include/ucore/supervisor.h @@ -0,0 +1,22 @@ +#ifndef UCORE_SUPERVISOR_H_ +#define UCORE_SUPERVISOR_H_ + +/** Supervises this process - restart if it dies. + * + * The program should call supervise() at a pristine state (all relevant file descriptors closed, etc). + * supervise() will return 0 if all is ok, and the program can continue.(returning != 0 means + * the supervisor couldn't start, and the calling process must exit) + * + * This will run the actual process as a child process. If that child process dies by any means, + * supervise will create another child process, where supervise() returns 0 again. + * + * To kill the supervisor process and the supervised process, send SIGERM/SIGHUP/SIGINT to the + * parent supervisor process. + * Sending SIGUSR1 to the parent supervisor process will terminate the current child process and + * restart it. + * + * The supervised child process must exit if it recieves a SIGTERM + * @return 0 on success, non-zero on failure (e.g. fork() fails)*/ +int uc_supervise(void); + +#endif diff --git a/include/ucore/timers.h b/include/ucore/timers.h index f4769e3..6aa34b5 100644 --- a/include/ucore/timers.h +++ b/include/ucore/timers.h @@ -6,6 +6,11 @@ #include #include "rbtree.h" +/** @addtogroup Timers + * @{ + * Timer management for managing timeouts + */ + #ifdef __cplusplus extern "C" { #endif @@ -147,4 +152,5 @@ int uc_timers_run(struct UCTimers *timers); } #endif +/** @{ (addtogroup) */ #endif diff --git a/include/ucore/utils.h b/include/ucore/utils.h index 9d1e86c..aad8379 100644 --- a/include/ucore/utils.h +++ b/include/ucore/utils.h @@ -48,12 +48,14 @@ //struct foo *f = CONTAINER_OF(p, struct foo, zap); #define CONTAINER_OF(ptr, type, member) ({ \ typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (unsigned char *)__mptr - offsetof(type, member) ); }) + (type *)((void *)(unsigned char *)__mptr - offsetof(type, member) ); }) + +//the void* cast is to suppress alignment warnings //Same as CONTAINER_OF, but for a const pointer to avoid gcc warnings.. #define CONST_CONTAINER_OF(ptr, type, member) ({ \ const typeof( ((const type *)0)->member ) *__mptr = (ptr); \ - (const type *)( (const unsigned char *)__mptr - offsetof(const type, member) ); }) + (const type *)((const void *)(const unsigned char *)__mptr - offsetof(const type, member) ); }) /** Align a value. * @param val value to align diff --git a/src/backtrace.c b/src/backtrace.c index 58e1fb2..69885b5 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -35,16 +35,18 @@ void uc_backtrace_buf(char *buf, size_t len) buf[0] = 0; lines = backtrace_symbols(buffer, ptrs); - for (i = 1; i < ptrs; i++) { + if (lines != NULL) { + for (i = 1; i < ptrs; i++) { - int rc = snprintf(buf, remaining, "%s\n", lines[i]); - - if (rc <= 0 || (size_t)rc >= remaining) { - break; + int rc = snprintf(buf, remaining, "%s\n", lines[i]); + + if (rc <= 0 || (size_t)rc >= remaining) { + break; + } + + remaining -= rc; + buf += rc; } - - remaining -= rc; - buf += rc; } free(lines); diff --git a/src/base64_dec.c b/src/base64_dec.c deleted file mode 100644 index 5ac2e98..0000000 --- a/src/base64_dec.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include "ucore/string.h" - -static const uint8_t basecode[128] = -{ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff -}; - -#define deccode(c) ((c > 127) ? 0xff : basecode[(c)]) - -size_t -uc_base64_dec(const unsigned char *data, size_t len, unsigned char *result) -{ - size_t i = 0, j = 0, pad; - uint8_t c[4]; - - while ((i + 3) < len) { - - pad = 0; - c[0] = deccode(data[i ]); pad += (c[0] == 0xff); - c[1] = deccode(data[i+1]); pad += (c[1] == 0xff); - c[2] = deccode(data[i+2]); pad += (c[2] == 0xff); - c[3] = deccode(data[i+3]); pad += (c[3] == 0xff); - switch (pad) { - case 1: - result[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); - result[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); - result[j] = (c[2] & 0x03) << 6; - break; - case 2: - result[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); - result[j] = (c[1] & 0x0f) << 4; - break; - default: - result[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); - result[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); - result[j++] = ((c[2] & 0x03) << 6) | (c[3] & 0x3f); - } - - i += 4; - } - - return j; -} - diff --git a/src/base64_enc.c b/src/base64_enc.c deleted file mode 100644 index bc8126e..0000000 --- a/src/base64_enc.c +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include "ucore/string.h" - -static const char basecode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -#define PAD '=' - -size_t -uc_base64_enc(const unsigned char *data,size_t len,unsigned char *result) -{ - - size_t i = 0, j = 0; - size_t pad; - - - while (i < len) { - pad = 3 - (len - i); - switch(pad) { - case 2: - result[j] = basecode[data[i]>>2]; - result[j+1] = basecode[(data[i] & 0x03) << 4]; - result[j+2] = PAD; - result[j+3] = PAD; - break; - case 3: - result[j ] = basecode[data[i]>>2]; - result[j+1] = basecode[((data[i] & 0x03) << 4) | ((data[i+1] & 0xf0) >> 4)]; - result[j+2] = basecode[(data[i+1] & 0x0f) << 2]; - result[j+3] = PAD; - break; - default: - result[j ] = basecode[data[i]>>2]; - result[j+1] = basecode[((data[i] & 0x03) << 4) | ((data[i+1] & 0xf0) >> 4)]; - result[j+2] = basecode[((data[i+1] & 0x0f) << 2) | ((data[i+2] & 0xc0) >> 6)]; - result[j+3] = basecode[data[i+2] & 0x3f]; - break; - } - j += 4; - i += 3; - } - - return j; -} diff --git a/src/dbuf.c b/src/dbuf.c new file mode 100644 index 0000000..c5cd1ce --- /dev/null +++ b/src/dbuf.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include "ucore/dbuf.h" + +void +uc_dbuf_free(DBuf *buf) +{ + if (buf != NULL) + free(buf->bufstart); +} + +int +uc_dbuf_init(DBuf *buf, size_t initlen) +{ + unsigned char *tmp = malloc(initlen); + if (tmp == NULL) + return -1; + buf->start = buf->bufstart = buf->end = tmp; + buf->bufend = tmp + initlen; + + return 0; +} + +size_t +uc_dbuf_len(DBuf *buf) +{ + return buf->end - buf->start; +} + +size_t +uc_dbuf_buflen(DBuf *buf) +{ + return buf->bufend - buf->bufstart; +} + +size_t +uc_dbuf_remaining(DBuf *buf) +{ + return buf->bufend - buf->end; +} + +int +uc_dbuf_ensure(DBuf *buf, size_t capacity) +{ + size_t sz, remaining, len; + + remaining = uc_dbuf_remaining(buf); + if (remaining >= capacity) + return 0; + len = uc_dbuf_len(buf); + sz = buf->start - buf->bufstart; + + if (buf->bufstart != buf->start) + memmove(buf->bufstart, buf->start, len); + + if (sz + remaining < capacity) { + size_t newlen = uc_dbuf_buflen(buf) + capacity; + void *tmp = realloc(buf->bufstart, newlen); + if (tmp == NULL) + return 1; + buf->bufstart = tmp; + buf->bufend = buf->bufstart + newlen; + + } + buf->start = buf->bufstart; + buf->end = buf->start + len; + + return 0; +} + +void +uc_dbuf_trim(DBuf *buf) +{ + size_t len = uc_dbuf_len(buf); + void *tmp; + + if (len == 0) + return; + + if (buf->bufstart != buf->start) + memmove(buf->bufstart, buf->start, len); + + tmp = realloc(buf->bufstart, len); + if (tmp != NULL) + buf->bufstart = tmp; + + buf->start = buf->bufstart; + buf->bufend = buf->end = buf->bufstart + len; +} + +int +uc_dbuf_take(DBuf *buf, size_t len) +{ + size_t buflen = uc_dbuf_len(buf); + + if (len == buflen) { + buf->start = buf->end = buf->bufstart; + } else if (len < buflen) { + buf->start = buf->start + len; + } else { + //should maybe assert. This is a serious offence + return 1; + } + + return 0; +} + +void +uc_dbuf_added(DBuf *buf, size_t len) +{ + assert(len <= uc_dbuf_remaining(buf)); + buf->end += len; +} diff --git a/src/fd_utils.c b/src/fd_utils.c index 9e7d539..a3e65c8 100644 --- a/src/fd_utils.c +++ b/src/fd_utils.c @@ -2,20 +2,23 @@ #include #include #include +#include #include #include #include "ucore/fd_utils.h" -int set_nonblocking(int fd) +int uc_set_nonblocking(int fd) { int flags = fcntl(fd,F_GETFL,NULL); + if(flags < 0 ) { return flags; } + return fcntl(fd,F_SETFL,flags | O_NONBLOCK); } -int set_blocking(int fd) +int uc_set_blocking(int fd) { int flags = fcntl(fd,F_GETFL,NULL); if(flags < 0 ) { @@ -25,43 +28,74 @@ int set_blocking(int fd) return fcntl(fd,F_SETFL,flags&~O_NONBLOCK); } -int set_reuseaddr(int sockfd) +int uc_set_reuseaddr(int sockfd) { int opt = 1; return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)); - } -int set_tcp_keepalive(int sockfd) +int uc_set_tcp_keepalive(int sockfd) { int optval = 1; + return setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); } +int uc_set_tcp_keepalive_cfg(int sockfd, const struct UCKeepConfig *cfg) +{ + int rc; -int set_send_timeout(int sockfd,long timeoutms) + //first turn on keepalive + rc = uc_set_tcp_keepalive(sockfd); + if (rc != 0) { + return rc; + } + + //set the keepalive options + rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &cfg->keepcnt, sizeof cfg->keepcnt); + if (rc != 0) { + return rc; + } + + rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &cfg->keepidle, sizeof cfg->keepintvl); + if (rc != 0) { + return rc; + } + + rc = setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &cfg->keepintvl, sizeof cfg->keepintvl); + if (rc != 0) { + return rc; + } + + return 0; +} + +int uc_set_send_timeout(int sockfd,long timeoutms) { struct timeval so_sndtimeo = {timeoutms/1000,(timeoutms%1000)*1000}; return setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &so_sndtimeo, sizeof(so_sndtimeo)); } -int set_receive_timeout(int sockfd,long timeoutms) +int uc_set_receive_timeout(int sockfd,long timeoutms) { struct timeval so_sndtimeo = {timeoutms/1000,(timeoutms%1000)*1000}; return setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &so_sndtimeo, sizeof(so_sndtimeo)); } -int -set_ip_dscp(int fd, unsigned int dscp) { + +int uc_set_ip_dscp(int fd, unsigned int dscp) +{ int val; + //only 6 bits for dscp if (dscp > 63) { errno = EINVAL; return -1; } val = dscp << 2; + return setsockopt(fd, IPPROTO_IP, IP_TOS, &val, sizeof val); } diff --git a/src/iomux_epoll.c b/src/iomux_epoll.c index 22f964b..65a9eaf 100644 --- a/src/iomux_epoll.c +++ b/src/iomux_epoll.c @@ -183,7 +183,7 @@ again: fd->callback(mux_, fd, events); //be sure not to use fd after the callback, it might been removed. - mux->pending_events[i].data.ptr = NULL; + mux->pending_events[i].data.ptr = NULL; //clear from pending list event_cnt++; } diff --git a/src/iomux_select.c b/src/iomux_select.c index 8b67414..ad39cf9 100644 --- a/src/iomux_select.c +++ b/src/iomux_select.c @@ -168,17 +168,18 @@ again: } if (FD_ISSET(mux->descriptors[i]->fd, &read_set)) { + rc--; events |= MUX_EV_READ; } if (FD_ISSET(mux->descriptors[i]->fd, &write_set)) { + rc--; events |= MUX_EV_WRITE; } if (events) { assert(mux->descriptors[i]->callback != NULL); mux->descriptors[i]->callback(mux_, mux->descriptors[i], events); - rc--; event_cnt++; } } diff --git a/src/mbuf.c b/src/mbuf.c new file mode 100644 index 0000000..6d735ab --- /dev/null +++ b/src/mbuf.c @@ -0,0 +1,119 @@ +#include +#include +#include "ucore/mbuf.h" + +uint8_t *uc_mbuf_put(struct MBuf *mbuf, uint32_t size) +{ + uint8_t *data = mbuf->tail; + + if (uc_mbuf_tailroom(mbuf) < size) { + return NULL; + } + + mbuf->tail += size; + + return data; +} + +uint8_t *uc_mbuf_push(struct MBuf *mbuf, uint32_t size) +{ + if (uc_mbuf_headroom(mbuf) < size) { + return NULL; + } + + mbuf->head -= size; + + return mbuf->head; +} + +uint8_t *uc_mbuf_pull(struct MBuf *mbuf, uint32_t size) +{ + uint8_t *data = mbuf->head; + + if (uc_mbuf_len(mbuf) < size) { + return NULL; + } + + mbuf->head += size; + + return data; +} + +void uc_mbuf_init(struct MBuf *mbuf, uint8_t *buf, uint32_t len, uint32_t headroom, uint32_t flags) +{ + mbuf->bufstart = buf; + mbuf->next = NULL; + mbuf->p1 = mbuf->p2 = mbuf->p3 = mbuf->p4 = NULL; + mbuf->cookie = NULL; + mbuf->flags = flags; + mbuf->allocated = len + headroom; + mbuf->head = mbuf->bufstart + headroom; + mbuf->tail = mbuf->head; +} + +struct MBuf *uc_mbuf_new_hr(uint32_t len, uint32_t headroom) +{ + struct MBuf *mbuf; + uint8_t *buf; + + mbuf = malloc(sizeof *mbuf); + if (mbuf == NULL) + return mbuf; + + buf = malloc(len + headroom); + if (buf == NULL) { + free(mbuf); + return NULL; + } + + uc_mbuf_init(mbuf, buf, len, headroom, UC_MBUF_FL_MALLOC); + + return mbuf; +} + +struct MBuf *uc_mbuf_new_inline_hr(uint32_t len, uint32_t headroom) +{ + struct MBuf *mbuf; + + mbuf = malloc(sizeof *mbuf + len + headroom); + if (mbuf == NULL) + return mbuf; + + uc_mbuf_init(mbuf, mbuf->inline_data, len, headroom, UC_MBUF_FL_MALLOC_INLINE); + + return mbuf; +} + +void uc_mbuf_free(struct MBuf *mbuf) +{ + if (mbuf->flags & (UC_MBUF_FL_MALLOC | UC_MBUF_FL_MALLOC_BUF)) + free(mbuf->bufstart); + + if (mbuf->flags & (UC_MBUF_FL_MALLOC | UC_MBUF_FL_MALLOC_INLINE)) + free(mbuf); +} + +void uc_mbuf_zero(struct MBuf *mbuf) +{ + memset(mbuf->bufstart, 0, mbuf->allocated); +} + +struct MBuf *uc_mbuf_copy_data(struct MBuf *mbuf) +{ + uint32_t len; + struct MBuf *copy; + uint8_t *data; + + len = uc_mbuf_len(mbuf); + + copy = uc_mbuf_new_hr(len, uc_mbuf_headroom(mbuf)); + if (copy == NULL) { + return NULL; + } + + data = uc_mbuf_put(copy, len); + memcpy(data, mbuf->head, len); + + return copy; +} + diff --git a/src/strvec.c b/src/strvec.c new file mode 100644 index 0000000..079cbdd --- /dev/null +++ b/src/strvec.c @@ -0,0 +1,107 @@ +#include +#include +#include "ucore/strvec.h" + +void uc_strv_init(struct UCStrv *strv) +{ + strv->cnt = 0; + strv->allocated = 0; + strv->strings = NULL; +} + +int uc_strv_grow(struct UCStrv *strv, size_t cnt) +{ + + size_t allocate = strv->allocated + cnt; + void *tmp = realloc(strv->strings, sizeof *strv->strings * allocate); + + if (tmp == NULL) { + return -1; + } + strv->strings = tmp; + strv->allocated = allocate; + + return 0; +} + +int uc_strv_expand(struct UCStrv *strv) +{ + int rc = 0; + + if (strv->cnt >= strv->allocated) { + rc = uc_strv_grow(strv, 1); + } + + return rc; +} + +int uc_strv_append(struct UCStrv *strv, const char *str) +{ + int rc; + char *dup; + + rc = uc_strv_expand(strv); + if (rc != 0) { + return rc; + } + + dup = strdup(str); + if (dup == NULL) { + return -2; + } + + strv->strings[strv->cnt] = dup; + strv->cnt++; + + return 0; +} + +int uc_strv_append_nocopy(struct UCStrv *strv, char *str) +{ + int rc; + rc = uc_strv_expand(strv); + + if (rc != 0) { + return rc; + } + + strv->strings[strv->cnt] = str; + strv->cnt++; + + return 0; +} + +int uc_strv_null_terminate(struct UCStrv *strv) +{ + int rc; + + rc = uc_strv_expand(strv); + if (rc != 0) { + return rc; + } + + strv->strings[strv->cnt] = NULL; + strv->cnt++; + + return 0; +} + +void uc_strv_clear(struct UCStrv *strv) +{ + size_t i; + + for (i = 0; i < strv->cnt; i++) { + free(strv->strings[i]); + strv->strings[i] = NULL; + } + + strv->cnt = 0; +} + +void uc_strv_destroy(struct UCStrv *strv) +{ + uc_strv_clear(strv); + free(strv->strings); + strv->strings = NULL; +} + diff --git a/src/supervisor.c b/src/supervisor.c new file mode 100644 index 0000000..b5f41d8 --- /dev/null +++ b/src/supervisor.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define CLEAN_EXIT 11 +#define BAD_EXIT 22 +#define RESTART 33 + +//seconds to wait before restarting the process +#define RESTART_GRACE 1 + +static volatile sig_atomic_t restart_flag = 0; +static volatile sig_atomic_t exit_flag = 0; + +static void restart_handler(int sig) +{ + (void)sig; + restart_flag = 1; +} + +static void exit_handler(int sig) +{ + (void)sig; + exit_flag = 1; +} + +static void kill_supervised_process(pid_t pid) +{ + kill(pid, SIGTERM); + /* Todo.. verify we killed it, otherwise take more + * desperate measures*/ +} + +static int watch(pid_t pid) +{ + int status; + pid_t rc; + +again: + rc = waitpid(pid, &status, 0); + if (rc == -1 && errno == EINTR) { + if (restart_flag) { + restart_flag = 0; + kill_supervised_process(pid); + /* next waitpid should notify us of the death and + * we'll restart the process */ + goto again; + } else if (exit_flag) { + exit_flag = 0; + return CLEAN_EXIT; + } + + goto again; + + } else if (rc == -1) { + return BAD_EXIT; + } + + if (WIFSIGNALED(status)) { + syslog(LOG_ERR, "supervised process(pid %ld) exited cause of signal %d, status = 0x%X", (long)pid, WTERMSIG(status), status); + return RESTART; + } else if (WIFEXITED(status)) { + syslog(LOG_ERR, "supervised process(pid %ld) exited with code %d, status = 0x%X", (long)pid, WTERMSIG(status), status); + return RESTART; + } + + /* Ok, can we ever get here ? */ + + syslog(LOG_ERR, "waitpid of supervised process(pid %ld) status = 0x%X", (long)pid, status); + if (kill(pid, 0) == 0) { + syslog(LOG_ERR, "supervised process(pid %ld) seems to still be alive, not restarting", (long)pid); + goto again; + } + + return RESTART; +} + +static struct sigaction orig_usr1_sa; +static struct sigaction orig_term_sa; +static struct sigaction orig_hup_sa; +static struct sigaction orig_int_sa; + +static void restore_signal_handlers(void) +{ + sigaction(SIGUSR1, &orig_usr1_sa, NULL); + sigaction(SIGTERM, &orig_term_sa, NULL); + sigaction(SIGHUP, &orig_hup_sa, NULL); + sigaction(SIGINT, &orig_int_sa, NULL); +} + +static void install_signal_handlers(void) +{ + struct sigaction act; + memset(&act, 0, sizeof act); + + act.sa_handler = restart_handler; + sigaction(SIGUSR1, &act, &orig_usr1_sa); + + act.sa_handler = exit_handler; + sigaction(SIGTERM, &act, &orig_term_sa); + sigaction(SIGHUP, &act, &orig_hup_sa); + sigaction(SIGINT, &act, &orig_int_sa); +} + +//See notes in supervisor.h +int uc_supervise(void) +{ + pid_t pid; + + pid = fork(); + if (pid == -1) { + syslog(LOG_ERR, "Cannot create supervisor : %s", strerror(errno)); + return -1; + } else if (pid == 0) { + return 0; + } + + //close_files(); + install_signal_handlers(); + + syslog(LOG_INFO, "Supervising pid %ld", (long)pid); + + for (;;) { + int cmd = watch(pid); + switch (cmd) { + + case CLEAN_EXIT: + syslog(LOG_INFO, "Terminating supervised pid %lu", (unsigned long)pid); + kill_supervised_process(pid); + wait(NULL); + syslog(LOG_INFO, "Exiting"); + exit(0); + break; + + case BAD_EXIT: + syslog(LOG_ERR, "watch() error, exiting"); + exit(55); + break; + + case RESTART: { + pid_t new_pid = -1; + + while (new_pid == -1) { + sleep(RESTART_GRACE); + syslog(LOG_INFO, "Restarting supervised pid %lu", (unsigned long)pid); + closelog(); + new_pid = fork(); + if (new_pid == -1) { + syslog(LOG_INFO, "Cannot restart. %s", strerror(errno)); + } else if (new_pid == 0) { //return so the original process continues + restore_signal_handlers(); + return 0; + } + } + + pid = new_pid; + syslog(LOG_INFO, "Supervising new pid %lu", (unsigned long)new_pid); + } + + break; + + default: + exit(38); + break; + } + + } + + return 0x76; +} + + diff --git a/src/timers.c b/src/timers.c index b89381b..ed399dc 100644 --- a/src/timers.c +++ b/src/timers.c @@ -19,11 +19,11 @@ static int timers_cmp(const RBNode *a_, const RBNode *b_) } else if (a->seq > b->seq) { return 1; } else { //should never happen - //assert(0); + assert(0); return 0; } } - //assert(0); + assert(0); return 0; //silence compiler } @@ -56,8 +56,9 @@ void uc_timers_add(struct UCTimers *timers, struct UCTimer *timer, int sec, int struct timeval tmp; assert(timer->callback != NULL); - if (timer->callback == NULL) //must be set. + if (timer->callback == NULL) { //must be set. return; + } tmp.tv_sec = sec; tmp.tv_usec = usec; diff --git a/src/tval.c b/src/tval.c index b2a2eea..a2577e8 100644 --- a/src/tval.c +++ b/src/tval.c @@ -6,8 +6,10 @@ uc_tvadd(struct timeval *dst, const struct timeval *a, const struct timeval *b) dst->tv_sec = a->tv_sec + b->tv_sec; dst->tv_usec = a->tv_usec + b->tv_usec; - if (dst->tv_usec >= 1000000) - dst->tv_sec++, dst->tv_usec -= 1000000; + if (dst->tv_usec >= 1000000) { + dst->tv_sec++; + dst->tv_usec -= 1000000; + } return dst; } @@ -18,8 +20,10 @@ uc_tvsub(struct timeval *dst, const struct timeval *a, const struct timeval *b) dst->tv_sec = a->tv_sec - b->tv_sec; dst->tv_usec = a->tv_usec - b->tv_usec; - if (dst->tv_usec < 0) - dst->tv_sec--, dst->tv_usec += 1000000; + if (dst->tv_usec < 0) { + dst->tv_sec--; + dst->tv_usec += 1000000; + } return dst; } @@ -41,7 +45,7 @@ uc_tvcmp(const struct timeval *a, const struct timeval *b) } struct timeval * -uc_to2tv(struct timeval *dst, unsigned long ms) +uc_ms2tv(struct timeval *dst, unsigned long long ms) { dst->tv_sec = ms / 1000; dst->tv_usec = ms % 1000 * 1000; diff --git a/test/test_bitvec.c b/test/test_bitvec.c index 629c241..3805ebb 100644 --- a/test/test_bitvec.c +++ b/test/test_bitvec.c @@ -66,6 +66,8 @@ START_TEST (test_bitvec_setall_clearall) for (i = 0; i < sizeof(unsigned long) * 8; i++) fail_if(uc_bv_get_bit(v, i) != 0 , "bit %zu is not 0", i); + uc_bv_free(v); + END_TEST Suite *bitvec_suite(void) diff --git a/test/test_dbuf.c b/test/test_dbuf.c new file mode 100644 index 0000000..fbeed99 --- /dev/null +++ b/test/test_dbuf.c @@ -0,0 +1,97 @@ +#include +#include +#include + + +START_TEST (test_dbuf) + DBuf buf; + int rc; + + rc = uc_dbuf_init(&buf, 10); + + fail_if(rc != 0); + fail_if(uc_dbuf_buflen(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 10); + + strcpy((char *)buf.end, "12345679"); + uc_dbuf_added(&buf, 10); + fail_if(uc_dbuf_len(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 0); + + rc = uc_dbuf_ensure(&buf, 110); + fail_if(rc != 0); + fail_if(uc_dbuf_remaining(&buf) != 110); + fail_if(uc_dbuf_buflen(&buf) != 120); + + rc = uc_dbuf_ensure(&buf, 5); + fail_if(rc != 0); + strcpy((char *)buf.end, "ABCD"); + uc_dbuf_added(&buf, 5); + uc_dbuf_take(&buf, 10); + fail_if(uc_dbuf_remaining(&buf) != 105); + fail_if(uc_dbuf_len(&buf) != 5); + ck_assert_str_eq((char *)buf.start, "ABCD"); + + uc_dbuf_take(&buf, 5); + + fail_if(uc_dbuf_remaining(&buf) != 120); + fail_if(uc_dbuf_buflen(&buf) != 120); + fail_if(uc_dbuf_len(&buf) != 0); + + strcpy((char *)buf.end, "EFGH"); + uc_dbuf_added(&buf, 5); + uc_dbuf_trim(&buf); + fail_if(uc_dbuf_buflen(&buf) != 5); + fail_if(uc_dbuf_len(&buf) != 5); + + uc_dbuf_take(&buf, 1); + rc = uc_dbuf_ensure(&buf, 10); + fail_if(rc != 0); + ck_assert_str_eq((char *)buf.start, "FGH"); + + uc_dbuf_take(&buf, 1); + uc_dbuf_trim(&buf); + ck_assert_str_eq((char *)buf.start, "GH"); + fail_if(uc_dbuf_buflen(&buf) != 3); + fail_if(uc_dbuf_len(&buf) != 3); + + + + uc_dbuf_free(&buf); +END_TEST + +START_TEST (test_dbuf_take_fail) + DBuf buf; + int rc; + + rc = uc_dbuf_init(&buf, 10); + + fail_if(rc != 0); + fail_if(uc_dbuf_buflen(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 10); + + strcpy((char *)buf.end, "12345679"); + uc_dbuf_added(&buf, 10); + fail_if(uc_dbuf_len(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 0); + + rc = uc_dbuf_take(&buf, 11); + fail_if(rc == 0); + + uc_dbuf_free(&buf); +END_TEST + + + +Suite *dbuf_suite(void) +{ + Suite *s = suite_create("dbuf"); + TCase *tc = tcase_create("dbuf tests"); + tcase_add_test(tc, test_dbuf); + tcase_add_test(tc, test_dbuf_take_fail); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_hex.c b/test/test_hex.c index 87ca984..39250c9 100644 --- a/test/test_hex.c +++ b/test/test_hex.c @@ -164,6 +164,18 @@ START_TEST (test_uc_hex_encode_delim_zero_len) END_TEST +START_TEST (test_uc_hex_decode_lowercase) + char hex[] = "1f"; + uint8_t binary[2] = {0x11,0x22}; + uint8_t *res; + + res = uc_hex_decode(hex, 2, binary); + + fail_if(binary[0] != 0x1f); + fail_if(binary[1] != 0x22); + fail_if(res != binary + 1, "binary %p, res %p", &binary[0], res); + +END_TEST Suite *hex_suite(void) { Suite *s = suite_create("hex"); @@ -185,6 +197,8 @@ Suite *hex_suite(void) tcase_add_test(tc, test_uc_hex_decode_invalid); tcase_add_test(tc, test_uc_hex_decode_empty_string); + tcase_add_test(tc, test_uc_hex_decode_lowercase); + suite_add_tcase(s, tc); diff --git a/test/test_human_bytesz.c b/test/test_human_bytesz.c new file mode 100644 index 0000000..c3819d9 --- /dev/null +++ b/test/test_human_bytesz.c @@ -0,0 +1,85 @@ +#include +#include + + +START_TEST (test_human_bytesz_bytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(999, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "999 b"); +END_TEST + +START_TEST (test_human_bytesz_bytes_0) + char result[128]; + char *rc; + + rc = uc_human_bytesz(0, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "0 b"); +END_TEST + +START_TEST (test_human_bytesz_kilobytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(64000, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "64.00 kB"); +END_TEST + +START_TEST (test_human_bytesz_megabytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(6409000, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "6.41 MB"); +END_TEST + +START_TEST (test_human_bytesz_gigabytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(126999400000, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "127.00 GB"); +END_TEST + +START_TEST (test_human_bytesz_terrabytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(12453400030000, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "12.45 TB"); +END_TEST + +START_TEST (test_human_bytesz_petabytes) + char result[128]; + char *rc; + + rc = uc_human_bytesz(~0LL, result, sizeof result); + fail_if(rc == NULL); + ck_assert_str_eq(result, "18446.74 PB"); +END_TEST + + +Suite *human_bytesz_suite(void) +{ + Suite *s = suite_create("human_bytesz"); + TCase *tc = tcase_create("human_bytesz tests"); + tcase_add_test(tc, test_human_bytesz_bytes); + tcase_add_test(tc, test_human_bytesz_bytes_0); + tcase_add_test(tc, test_human_bytesz_kilobytes); + tcase_add_test(tc, test_human_bytesz_megabytes); + tcase_add_test(tc, test_human_bytesz_gigabytes); + tcase_add_test(tc, test_human_bytesz_terrabytes); + tcase_add_test(tc, test_human_bytesz_petabytes); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_logging.c b/test/test_logging.c index 76d1919..ea42309 100644 --- a/test/test_logging.c +++ b/test/test_logging.c @@ -50,6 +50,7 @@ START_TEST (test_logfile_location) dest = uc_log_new_file(FILE_NAME, UC_LL_DEBUG, 1); fail_if(dest == NULL); uc_log_add_destination(dest); + uc_log_add_destination(dest); //adding this twice should do nothing UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson 2\n"); @@ -62,6 +63,54 @@ START_TEST (test_logfile_location) fail_if(st.st_size != 380, "was %ld\n", (long)st.st_size); END_TEST +START_TEST (test_logfile_delete_destination) + + struct uc_log_module m = { + .id = 0, + .short_name = "MOD1", + .long_name = "Module 1", + .log_level = UC_LL_DEBUG, + }; + struct uc_log_modules mods = { + .mods = &m, + .cnt = 1, + }; + struct stat st; + struct uc_log_destination *dest; + int rc; + + uc_log_init(&mods); + + dest = uc_log_new_file(FILE_NAME, UC_LL_DEBUG, 1); + fail_if(dest == NULL); + uc_log_add_destination(dest); + uc_log_add_destination(dest); //adding this twice should do nothing + + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson 2\n"); + UC_LOGF(UC_LL_INFO, 0, "Lorum ipson 3\n"); + UC_LOGF(UC_LL_WARNING, 0, "Lorum ipson 4\n"); + UC_LOGF(UC_LL_ERROR, 0, "Lorum ipson 5\n"); + + rc = stat(FILE_NAME, &st); + fail_if(rc != 0, "stat failed: %s\n", strerror(errno)); + fail_if(st.st_size != 380, "was %ld\n", (long)st.st_size); + + uc_log_delete_destination(dest); + + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson %d\n", 1); + UC_LOGF(UC_LL_DEBUG, 0, "Lorum ipson 2\n"); + UC_LOGF(UC_LL_INFO, 0, "Lorum ipson 3\n"); + UC_LOGF(UC_LL_WARNING, 0, "Lorum ipson 4\n"); + UC_LOGF(UC_LL_ERROR, 0, "Lorum ipson 5\n"); + + //since we deleted destinatiion, no more logging should appear in the file + rc = stat(FILE_NAME, &st); + fail_if(rc != 0, "stat failed: %s\n", strerror(errno)); + fail_if(st.st_size != 380, "was %ld\n", (long)st.st_size); + +END_TEST + START_TEST (test_logfile_no_location) struct uc_log_module m = { @@ -476,6 +525,7 @@ Suite *logging_suite(void) TCase *tc2 = tcase_create("ucore_logging2"); tcase_add_test(tc1, test_logfile_location); + tcase_add_test(tc1, test_logfile_delete_destination); tcase_add_test(tc1, test_logfile_no_location); tcase_add_test(tc1, test_logfile_loglevel_surpressed_dest); tcase_add_test(tc1, test_logfile_loglevel_surpressed_module); diff --git a/test/test_mbuf.c b/test/test_mbuf.c new file mode 100644 index 0000000..dfe8577 --- /dev/null +++ b/test/test_mbuf.c @@ -0,0 +1,266 @@ +#include +#include +#include + + +START_TEST (test_mbuf_new) + struct MBuf *mbuf; + + mbuf = uc_mbuf_new(110); + + fail_if(mbuf == NULL); + fail_if(uc_mbuf_len(mbuf) != 0); + fail_if(uc_mbuf_headroom(mbuf) != 0); + fail_if(uc_mbuf_tailroom(mbuf) != 110); + fail_if(mbuf->next != NULL); + fail_if(mbuf->p1 != NULL); + fail_if(mbuf->p2 != NULL); + fail_if(mbuf->p3 != NULL); + fail_if(mbuf->p4 != NULL); + fail_if(mbuf->cookie != NULL); + fail_if(mbuf->head != mbuf->tail); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_new_inline) + struct MBuf *mbuf; + + mbuf = uc_mbuf_new_inline(110); + + fail_if(mbuf == NULL); + fail_if(uc_mbuf_len(mbuf) != 0); + fail_if(uc_mbuf_headroom(mbuf) != 0); + fail_if(uc_mbuf_tailroom(mbuf) != 110); + fail_if(mbuf->next != NULL); + fail_if(mbuf->p1 != NULL); + fail_if(mbuf->p2 != NULL); + fail_if(mbuf->p3 != NULL); + fail_if(mbuf->p4 != NULL); + fail_if(mbuf->cookie != NULL); + fail_if(mbuf->head != mbuf->tail); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_new_headroom) + struct MBuf *mbuf; + + mbuf = uc_mbuf_new_hr(110,20); + + fail_if(mbuf == NULL); + fail_if(uc_mbuf_len(mbuf) != 0); + fail_if(uc_mbuf_headroom(mbuf) != 20); + fail_if(uc_mbuf_tailroom(mbuf) != 110); + fail_if(mbuf->next != NULL); + fail_if(mbuf->p1 != NULL); + fail_if(mbuf->p2 != NULL); + fail_if(mbuf->p3 != NULL); + fail_if(mbuf->p4 != NULL); + fail_if(mbuf->cookie != NULL); + fail_if(mbuf->head != mbuf->tail); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_put) + struct MBuf *mbuf; + uint8_t *data; + + mbuf = uc_mbuf_new_inline(111); + fail_if(mbuf == NULL); + + data = uc_mbuf_put(mbuf, 111); + fail_if(data == NULL); + fail_if(uc_mbuf_len(mbuf) != 111); + fail_if(uc_mbuf_tailroom(mbuf) != 0); + + memset(data, 0, 111); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_put_fail) + struct MBuf *mbuf; + uint8_t *data; + + mbuf = uc_mbuf_new_inline(10); + fail_if(mbuf == NULL); + + data = uc_mbuf_put(mbuf, 11); + fail_if(data != NULL); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_push) + struct MBuf *mbuf; + uint8_t *data; + + mbuf = uc_mbuf_new_hr(10,1); + fail_if(mbuf == NULL); + + data = uc_mbuf_push(mbuf, 1); + fail_if(data == NULL); + fail_if(uc_mbuf_len(mbuf) != 1); + fail_if(uc_mbuf_headroom(mbuf) != 0); + + *data = 0; + + uc_mbuf_free(mbuf); + +END_TEST + + +START_TEST (test_mbuf_push_fail) + struct MBuf *mbuf; + uint8_t *data; + + mbuf = uc_mbuf_new_hr(1,2); + fail_if(mbuf == NULL); + + data = uc_mbuf_push(mbuf, 3); + fail_if(data != NULL); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_pull) + struct MBuf *mbuf; + uint8_t *data1; + uint8_t *data2; + + mbuf = uc_mbuf_new_inline(10); + fail_if(mbuf == NULL); + + data1 = uc_mbuf_put(mbuf, 10); + fail_if(data1 == NULL); + fail_if(uc_mbuf_tailroom(mbuf) != 0); + fail_if(uc_mbuf_len(mbuf) != 10); + + data2 = uc_mbuf_pull(mbuf,10); + fail_if(data2 == NULL); + fail_if(data1 != data2); + fail_if(uc_mbuf_len(mbuf) != 0); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_pull_fail) + struct MBuf *mbuf; + uint8_t *data1; + uint8_t *data2; + + mbuf = uc_mbuf_new_inline(10); + fail_if(mbuf == NULL); + + data1 = uc_mbuf_put(mbuf, 10); + fail_if(data1 == NULL); + fail_if(uc_mbuf_len(mbuf) != 10); + + data2 = uc_mbuf_pull(mbuf,11); + fail_if(data2 != NULL); + fail_if(uc_mbuf_len(mbuf) != 10); + + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_copy) + struct MBuf *mbuf; + uint8_t *data1; + struct MBuf *copy; + + mbuf = uc_mbuf_new_inline_hr(10,5); + fail_if(mbuf == NULL); + + data1 = uc_mbuf_put(mbuf, 10); + fail_if(data1 == NULL); + + strcpy((char*)data1, "123456789"); + + copy = uc_mbuf_copy_data(mbuf); + fail_if(copy == NULL); + fail_if(uc_mbuf_len(copy) != 10); + fail_if(uc_mbuf_headroom(copy) != 5); + fail_if(memcmp(mbuf->head, copy->head, 10) != 0); + + uc_mbuf_free(mbuf); + uc_mbuf_free(copy); + +END_TEST + +START_TEST (test_mbuf_zero) + struct MBuf *mbuf; + uint8_t *data1; + + mbuf = uc_mbuf_new_inline(10); + fail_if(mbuf == NULL); + + data1 = uc_mbuf_put(mbuf, 10); + fail_if(data1 == NULL); + + strcpy((char*)data1, "123456789"); + uc_mbuf_zero(mbuf); + + fail_if(memcmp(mbuf->head, "\0\0\0\0\0\0\0\0\0\0", 10) != 0); + + uc_mbuf_free(mbuf); + +END_TEST + +START_TEST (test_mbuf_static) + + uint8_t data[1024]; + struct MBuf mbuf; + + uc_mbuf_init(&mbuf, data, 1000, 24, UC_MBUF_FL_STATIC); + + fail_if(uc_mbuf_len(&mbuf) != 0); + fail_if(uc_mbuf_headroom(&mbuf) != 24); + fail_if(uc_mbuf_tailroom(&mbuf) != 1000); + fail_if(mbuf.next != NULL); + fail_if(mbuf.p1 != NULL); + fail_if(mbuf.p2 != NULL); + fail_if(mbuf.p3 != NULL); + fail_if(mbuf.p4 != NULL); + fail_if(mbuf.cookie != NULL); + fail_if(mbuf.head != mbuf.tail); + fail_if(mbuf.head != &data[24]); + + uc_mbuf_free(&mbuf); + +END_TEST + + + +Suite *mbuf_suite(void) +{ + Suite *s = suite_create("mbuf"); + TCase *tc = tcase_create("mbuf tests"); + tcase_add_test(tc, test_mbuf_new); + tcase_add_test(tc, test_mbuf_new_inline); + tcase_add_test(tc, test_mbuf_new_headroom); + tcase_add_test(tc, test_mbuf_put); + tcase_add_test(tc, test_mbuf_put_fail); + tcase_add_test(tc, test_mbuf_push); + tcase_add_test(tc, test_mbuf_push_fail); + tcase_add_test(tc, test_mbuf_pull); + tcase_add_test(tc, test_mbuf_pull_fail); + tcase_add_test(tc, test_mbuf_copy); + tcase_add_test(tc, test_mbuf_zero); + tcase_add_test(tc, test_mbuf_static); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_read_file.c b/test/test_read_file.c index 0bb4990..5bef1f4 100644 --- a/test/test_read_file.c +++ b/test/test_read_file.c @@ -91,6 +91,65 @@ START_TEST (test_read_file_boundary) free(content); END_TEST +START_TEST (test_read_file_devnull) + char *content; + size_t len = 123; + + content = uc_read_file("/dev/null", &len, 1024); + fail_if(content == NULL); + fail_if(len != 0); + free(content); + +END_TEST + +START_TEST (test_read_file_too_big) + char *content; + const char *filename = "test_read_file_too_big"; + size_t len; + ssize_t rc; + + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664); + fail_if(fd == -1); + + rc = write(fd, "hello", 5); + fail_if(rc != 5, "rc was %ld", (long)rc); + + close(fd); + + content = uc_read_file(filename, &len, 4); + + fail_if(errno != EMSGSIZE); + fail_if(content != NULL); + + unlink(filename); +END_TEST + +START_TEST (test_read_file_big) + char *content; + const char *filename = "test_read_file_big"; + char buf[4096 * 16]; + size_t len; + ssize_t rc; + + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664); + fail_if(fd == -1); + memset(buf, 'a', sizeof buf); + + rc = write(fd, buf, sizeof buf); + fail_if(rc != sizeof buf, "rc was %ld", (long)rc); + + close(fd); + + content = uc_read_file(filename, &len, 4096*16); + fail_if(content == NULL); + fail_if(len != sizeof buf); + fail_if(memcmp(content, buf, sizeof buf) != 0); + + free(content); + + unlink(filename); +END_TEST + Suite *read_file_suite(void) { Suite *s = suite_create("read_file"); @@ -99,6 +158,10 @@ Suite *read_file_suite(void) tcase_add_test(tc, test_read_file_simple); tcase_add_test(tc, test_read_file_greater_than_max); tcase_add_test(tc, test_read_file_boundary); + tcase_add_test(tc, test_read_file_devnull); + tcase_add_test(tc, test_read_file_too_big); + tcase_add_test(tc, test_read_file_big); + suite_add_tcase(s, tc); diff --git a/test/test_runner.c b/test/test_runner.c index d85129a..3c38f59 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -19,6 +19,11 @@ extern Suite *clock_suite(void); extern Suite *seq_suite(void); extern Suite *sat_math_suite(void); extern Suite *timers_suite(void); +extern Suite *mbuf_suite(void); +extern Suite *human_bytesz_suite(void); +extern Suite *dbuf_suite(void); +extern Suite *sprintb_suite(void); +extern Suite *strv_suite(void); static suite_func suites[] = { bitvec_suite, @@ -33,7 +38,12 @@ static suite_func suites[] = { clock_suite, seq_suite, sat_math_suite, - timers_suite + timers_suite, + mbuf_suite, + human_bytesz_suite, + dbuf_suite, + sprintb_suite, + strv_suite }; int diff --git a/test/test_sprintb.c b/test/test_sprintb.c new file mode 100644 index 0000000..0a1f782 --- /dev/null +++ b/test/test_sprintb.c @@ -0,0 +1,97 @@ +#include +#include +#include + +//Note these tests assume +// char 8 bit +// short 16 bit +// int 32 bit +// long long 64 bit + +START_TEST (test_sprintb) + char result[128]; + char *rc; + + rc = uc_sprintb(result, 0); + ck_assert_str_eq(rc, "00000000000000000000000000000000"); + + rc = uc_sprintb(result, UINT_MAX); + ck_assert_str_eq(rc, "11111111111111111111111111111111"); + + rc = uc_sprintb(result, 0xE0A14341); + ck_assert_str_eq(rc, "11100000101000010100001101000001"); + + rc = uc_sprintb(result, 1); + ck_assert_str_eq(rc, "00000000000000000000000000000001"); + +END_TEST + +START_TEST (test_sprintbc) + char result[128]; + char *rc; + + rc = uc_sprintbc(result, 0); + ck_assert_str_eq(rc, "00000000"); + + rc = uc_sprintbc(result, UCHAR_MAX); + ck_assert_str_eq(rc, "11111111"); + + rc = uc_sprintbc(result, 0xE1); + ck_assert_str_eq(rc, "11100001"); + + rc = uc_sprintbc(result, 1); + ck_assert_str_eq(rc, "00000001"); + +END_TEST + +START_TEST (test_sprintbs) + char result[128]; + char *rc; + + rc = uc_sprintbs(result, 0); + ck_assert_str_eq(rc, "0000000000000000"); + + rc = uc_sprintbs(result, USHRT_MAX); + ck_assert_str_eq(rc, "1111111111111111"); + + rc = uc_sprintbs(result, 0xE0A1); + ck_assert_str_eq(rc, "1110000010100001"); + + rc = uc_sprintbs(result, 1); + ck_assert_str_eq(rc, "0000000000000001"); + +END_TEST + +START_TEST (test_sprintbll) + char result[128]; + char *rc; + + rc = uc_sprintbll(result, 0); + ck_assert_str_eq(rc, "0000000000000000000000000000000000000000000000000000000000000000"); + + rc = uc_sprintbll(result, ULLONG_MAX); + ck_assert_str_eq(rc, "1111111111111111111111111111111111111111111111111111111111111111"); + + rc = uc_sprintbll(result, 0xE0A14341E0A14341ULL); + ck_assert_str_eq(rc, "1110000010100001010000110100000111100000101000010100001101000001"); + + rc = uc_sprintbll(result, 1); + ck_assert_str_eq(rc, "0000000000000000000000000000000000000000000000000000000000000001"); + +END_TEST + +Suite *sprintb_suite(void) +{ + Suite *s = suite_create("sprintb"); + TCase *tc = tcase_create("sprintb tests"); + + tcase_add_test(tc, test_sprintb); + tcase_add_test(tc, test_sprintbc); + tcase_add_test(tc, test_sprintbs); + tcase_add_test(tc, test_sprintbll); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_strv.c b/test/test_strv.c new file mode 100644 index 0000000..d9745b2 --- /dev/null +++ b/test/test_strv.c @@ -0,0 +1,91 @@ +#include +#include +#include + + +START_TEST (test_strv_append) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + ck_assert_str_eq("string", strv.strings[0]); + + rc = uc_strv_append(&strv, "string2"); + fail_if(rc != 0); + fail_if(strv.cnt != 2); + ck_assert_str_eq("string2", strv.strings[1]); + + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_clear) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + + uc_strv_clear(&strv); + fail_if(strv.cnt != 0); + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_nocopy) + struct UCStrv strv; + char *str = strdup("string"); + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append_nocopy(&strv, str); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + fail_if(strv.strings[0] != str); + + uc_strv_destroy(&strv); + +END_TEST + +START_TEST (test_strv_null_terminate) + struct UCStrv strv; + int rc; + + uc_strv_init(&strv); + + rc = uc_strv_append(&strv, "string"); + fail_if(rc != 0); + fail_if(strv.cnt != 1); + + rc = uc_strv_null_terminate(&strv); + fail_if(rc != 0); + fail_if(strv.strings[strv.cnt] != NULL); + + uc_strv_destroy(&strv); + +END_TEST + + +Suite *strv_suite(void) +{ + Suite *s = suite_create("strvec"); + TCase *tc = tcase_create("strvec"); + + tcase_add_test(tc, test_strv_append); + tcase_add_test(tc, test_strv_clear); + tcase_add_test(tc, test_strv_nocopy); + tcase_add_test(tc, test_strv_null_terminate); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_timers.c b/test/test_timers.c index a563cd4..d0e81aa 100644 --- a/test/test_timers.c +++ b/test/test_timers.c @@ -160,6 +160,47 @@ START_TEST (test_timers_remove_from_callback) END_TEST +START_TEST (test_timers_add_remove) + struct UCoreSettableClock clk; + struct TimerTest tt; + struct UCTimers timers; + struct UCTimer timer1; + struct UCTimer timer2; + struct UCTimer timer3; + struct UCTimer timer4; + struct timeval first; + + common_init(&clk, &tt, &timers, &timer1); + memset(&timer2, 0, sizeof timer2); + memset(&timer3, 0, sizeof timer2); + memset(&timer4, 0, sizeof timer2); + + timer1.callback = timer_test_cb_add; + timer2.callback = timer_test_cb_add; + timer3.callback = timer_test_cb_add; + timer4.callback = timer_test_cb_add; + + uc_timers_add(&timers, &timer1, 10, 0); + uc_timers_add(&timers, &timer2, 5 , 0); + uc_timers_add(&timers, &timer3, 15, 1); + uc_timers_add(&timers, &timer4, 15, 1); + + fail_if(uc_timers_first(&timers, &first) != 0); + fail_if(first.tv_sec != TEST_TIMER_START + 5); + fail_if(first.tv_usec != 0); + + fail_if(uc_timers_count(&timers) != 4); + + uc_timers_remove(&timers, &timer1); + uc_timers_remove(&timers, &timer2); + uc_timers_remove(&timers, &timer3); + uc_timers_remove(&timers, &timer4); + + fail_if(uc_timers_count(&timers) != 0); + fail_if(uc_timers_first(&timers, &first) == 0); + +END_TEST + Suite *timers_suite(void) { @@ -168,6 +209,7 @@ Suite *timers_suite(void) tcase_add_test(tc, test_timers_simple); tcase_add_test(tc, test_timers_add_from_callback); tcase_add_test(tc, test_timers_remove_from_callback); + tcase_add_test(tc, test_timers_add_remove); suite_add_tcase(s, tc);