From 321cc67d890042025625df6a591fcfe9e62ec95d Mon Sep 17 00:00:00 2001 From: Oscar Olsson <osse.olsson@gmail.com> Date: Tue, 5 Mar 2013 13:00:23 +0100 Subject: [PATCH] Added changes from firefly --- compiler/build.xml | 4 +- lib/c/Makefile | 81 +++++++- lib/c/cppmacros.h | 8 + lib/c/experimental/ThrottleDrv/display.h | 7 +- lib/c/experimental/ThrottleDrv/ethernet_drv.c | 5 +- lib/c/experimental/ethaddr.c | 6 +- lib/c/experimental/udp_hack.c | 6 +- lib/c/labcomm.c | 176 ++++++++++++++--- lib/c/labcomm.h | 75 ++++++- lib/c/labcomm_mem_reader.c | 73 +++++++ lib/c/labcomm_mem_reader.h | 17 ++ lib/c/labcomm_mem_writer.c | 143 ++++++++++++++ lib/c/labcomm_mem_writer.h | 25 +++ lib/c/labcomm_private.h | 18 +- lib/c/test/test_labcomm.c | 187 ++++++++++++++++++ lib/c/test/test_labcomm_errors | Bin 0 -> 46272 bytes lib/c/test/test_labcomm_errors.c | 168 ++++++++++++++++ lib/c/test/test_labcomm_errors.h | 6 + lib/c/test/testdata/test_sample.lc | 1 + 19 files changed, 957 insertions(+), 49 deletions(-) create mode 100644 lib/c/cppmacros.h create mode 100644 lib/c/labcomm_mem_reader.c create mode 100644 lib/c/labcomm_mem_reader.h create mode 100644 lib/c/labcomm_mem_writer.c create mode 100644 lib/c/labcomm_mem_writer.h create mode 100644 lib/c/test/test_labcomm.c create mode 100755 lib/c/test/test_labcomm_errors create mode 100644 lib/c/test/test_labcomm_errors.c create mode 100644 lib/c/test/test_labcomm_errors.h create mode 100644 lib/c/test/testdata/test_sample.lc diff --git a/compiler/build.xml b/compiler/build.xml index 932ba04..ff8cf2a 100644 --- a/compiler/build.xml +++ b/compiler/build.xml @@ -4,7 +4,7 @@ clean - removes all generated files and class files Targets for working from Eclipse: gen - generates java files - genClean - removes all generated files and their class files + cleanGen - removes all generated files and their class files --> <project name="LabComm" default="build" basedir="."> @@ -77,7 +77,9 @@ classpath="tools/jastadd2.jar"/> <!-- delete all .class files recursively --> <delete> <fileset dir="." includes="**/*.class"/> + <fileset dir="." includes="labComm.jar"/> </delete> + </target> diff --git a/lib/c/Makefile b/lib/c/Makefile index 4d7117e..a17c72d 100644 --- a/lib/c/Makefile +++ b/lib/c/Makefile @@ -1,22 +1,85 @@ -CC = gcc -CFLAGS = -g -I . +## Macros -liblabcomm.a : labcomm.o labcomm_fd_reader_writer.o experimental/labcomm_udp_reader_writer.o \ - experimental/udp_hack.o experimental/ethaddr.o \ - experimental/labcomm_thr_reader_writer.o \ - experimental/ThrottleDrv/ethernet_drv.o experimental/ThrottleDrv/throttle_drv.o +# Use LLVM clang if it's found. +CC = $(shell hash clang 2>/dev/null && echo clang || echo gcc) +CFLAGS = -g -Wall -I . +LDFLAGS = -L . +LDLIBS_TEST = -lcunit -llabcomm + +OBJS= labcomm.o labcomm_fd_reader_writer.o labcomm_mem_reader.o labcomm_mem_writer.o +LABCOMMC_PATH=../../compiler +LABCOMMC_JAR=$(LABCOMMC_PATH)/labComm.jar + +TEST_DIR=test +TESTDATA_DIR=$(TEST_DIR)/testdata +TEST_GEN_DIR=$(TESTDATA_DIR)/gen + +CREATED_DIRS=$(TEST_DIR) $(TESTDATA_DIR) $(TEST_GEN_DIR) + +# Disable experimental objects by invoking make like `make -e LABCOMM_NO_EXPERIMENTAL=true` +ifneq ($(LABCOMM_NO_EXPERIMENTAL),true) + OBJS += experimental/udp_hack.o experimental/ethaddr.o \ + experimental/labcomm_thr_reader_writer.o \ + experimental/ThrottleDrv/ethernet_drv.o \ + experimental/ThrottleDrv/throttle_drv.o \ + experimental/labcomm_udp_reader_writer.o +endif + +## Targets + +.PHONY: all run-test clean distclean + +all: liblabcomm.a test/test_labcomm_errors + +liblabcomm.a: $(OBJS) ar -r liblabcomm.a $^ + labcomm.o : labcomm.c labcomm.h labcomm_private.h labcomm_fd_reader_writer.o : labcomm_fd_reader_writer.c labcomm_fd_reader_writer.h labcomm.h labcomm_private.h +labcomm_mem_reader.o: labcomm_fd_reader_writer.c labcomm_fd_reader_writer.h + +labcomm_mem_writer.o: labcomm_mem_writer.c labcomm_mem_writer.h cppmacros.h + ethaddr.o: ethaddr.c -%o: %c %h +$(CREATED_DIRS): + mkdir -p $@ + +run-test: $(TEST_DIR)/test_labcomm_errors |$(TEST_DIR) + test/test_labcomm_errors + +$(TEST_DIR)/test_labcomm_errors: $(TEST_DIR)/test_labcomm_errors.o liblabcomm.a |$(TEST_DIR) + $(CC) $(CFLAGS) $(LDFLAGS) -llabcomm -o $@ $^ + +$(TEST_DIR)/test_labcomm_errors.o: $(TEST_DIR)/test_labcomm_errors.c $(TEST_DIR)/test_labcomm_errors.h |$(TEST_DIR) + cd test; $(CC) $(CFLAGS) -I .. -c $(patsubst $(TEST_DIR)/%, %, $^) + +$(TEST_DIR)/test_labcomm.o: $(TEST_DIR)/test_labcomm.c $(TEST_GEN_DIR)/test_sample.h |$(TEST_DIR) + $(CC) -c $(CFLAGS) -o $@ $< + +$(TEST_DIR)/test_labcomm: $(TEST_DIR)/test_labcomm.o $(TEST_GEN_DIR)/test_sample.o liblabcomm.a + $(CC) $(CFLAGS) $(LDFLAGS) $(filter-out %.a,$^) $(LDLIBS) $(LDLIBS_TEST) -o $@ + +$(TEST_GEN_DIR)/%.c $(TEST_GEN_DIR)/%.h: $(TESTDATA_DIR)/%.lc $(LABCOMMC_JAR) |$(TEST_GEN_DIR) + java -jar $(LABCOMMC_JAR) --c=$(patsubst %.h,%.c,$@) --h=$(patsubst %.c,%.h,$@) $< + +$(LABCOMMC_JAR): + @echo "======Building LabComm compiler======" + cd $(LABCOMMC_PATH); ant jar + @echo "======End building LabComm compiler======" + +%.o: %.c %.h clean: - rm *.o experimental/*.o experimental/ThrottleDrv/*.o + $(RM) *.o + $(RM) experimental/*.o experimental/ThrottleDrv/*.o + $(RM) test/*.o + $(RM) test/*.gch + $(RM) test/test_labcomm_errors + $(RM) $(TEST_DIR)/test_labcomm distclean: clean - rm liblabcomm.a + $(RM) liblabcomm.a diff --git a/lib/c/cppmacros.h b/lib/c/cppmacros.h new file mode 100644 index 0000000..a3e446b --- /dev/null +++ b/lib/c/cppmacros.h @@ -0,0 +1,8 @@ +// C Preprocessor macros. +#ifndef CPP_MACROS_H +#define CPP_MACROS_H + +#define X_EMPTY(mac) var ## 1 +#define EMPTY(mac) X_EMPTY(mac) // Returns 1 if macro mac is empty. + +#endif diff --git a/lib/c/experimental/ThrottleDrv/display.h b/lib/c/experimental/ThrottleDrv/display.h index f8bed75..49c9cd4 100644 --- a/lib/c/experimental/ThrottleDrv/display.h +++ b/lib/c/experimental/ThrottleDrv/display.h @@ -1,7 +1,12 @@ #define PC_MODE #ifdef PC_MODE -#include <stdio.h> #define DISPLAY_ERR(s) perror(s); + +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif + #else #define DISPLAY_ERR(s) ; #endif diff --git a/lib/c/experimental/ThrottleDrv/ethernet_drv.c b/lib/c/experimental/ThrottleDrv/ethernet_drv.c index fe0004c..9b4126e 100644 --- a/lib/c/experimental/ThrottleDrv/ethernet_drv.c +++ b/lib/c/experimental/ThrottleDrv/ethernet_drv.c @@ -1,7 +1,6 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <stdio.h> #include <errno.h> #include <sys/socket.h> #include <sys/ioctl.h> @@ -14,6 +13,10 @@ #include "ethernet_drv.h" #include "display.h" +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif /** LOCAL FUNCTIONS **/ diff --git a/lib/c/experimental/ethaddr.c b/lib/c/experimental/ethaddr.c index b2207e5..5aed408 100644 --- a/lib/c/experimental/ethaddr.c +++ b/lib/c/experimental/ethaddr.c @@ -1,6 +1,10 @@ -#include <stdio.h> #include "ethaddr.h" +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif + #if ETH_ALEN != 6 #warning "Assumption that ETH_ALEN == 6 appears false. Here be dragons." #endif diff --git a/lib/c/experimental/udp_hack.c b/lib/c/experimental/udp_hack.c index f4670bb..1d26eb7 100644 --- a/lib/c/experimental/udp_hack.c +++ b/lib/c/experimental/udp_hack.c @@ -1,11 +1,15 @@ #include <arpa/inet.h> #include <netinet/in.h> -#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif + #define BUFLEN 512 #define NPACK 10 #define PORT 9930 diff --git a/lib/c/labcomm.c b/lib/c/labcomm.c index 34ce50b..76324da 100644 --- a/lib/c/labcomm.c +++ b/lib/c/labcomm.c @@ -1,18 +1,32 @@ #include <errno.h> #include <string.h> -#include <stdio.h> + #ifndef __VXWORKS__ -#include <strings.h> + #ifdef ARM_CORTEXM3_CODESOURCERY + #include <string.h> + #else + #include <strings.h> + #endif #endif -#include <stdlib.h> -#ifdef __VXWORKS__ -#if (CPU == PPC603) -#undef _LITTLE_ENDIAN + +#ifndef ARM_CORTEXM3_CODESOURCERY + #include <stdlib.h> #endif -#if (CPU == PENTIUM4) -#undef _BIG_ENDIAN + +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> #endif + +#ifdef __VXWORKS__ + #if (CPU == PPC603) + #undef _LITTLE_ENDIAN + #endif + #if (CPU == PENTIUM4) + #undef _BIG_ENDIAN + #endif #endif + #include "labcomm.h" #include "labcomm_private.h" @@ -35,6 +49,83 @@ typedef struct labcomm_decoder_context { labcomm_sample_entry_t *sample; } labcomm_decoder_context_t; +void labcomm_register_error_handler_encoder(struct labcomm_encoder *encoder, labcomm_error_handler_callback callback) +{ + encoder->on_error = callback; + encoder->writer.on_error = callback; +} + +void labcomm_register_error_handler_decoder(struct labcomm_decoder *decoder, labcomm_error_handler_callback callback) +{ + decoder->on_error = callback; + decoder->reader.on_error = callback; +} + +/* Error strings. _must_ be the same order as in enum labcomm_error */ +const char *labcomm_error_strings[] = { + "Enum begin guard. DO NO use this as an error.", + "Encoder has no registration for this signature.", + "Encoder is missing do_register", + "Encoder is missing do_encode", + "The labcomm buffer is full and it.", + "Decoder is missing do_register", + "Decoder is missing do_decode_one", + "Decoder: Unknown datatype", + "Decoder: index mismatch", + "Decoder: type not found", + "This function is not yet implemented.", + "User defined error.", + "Could not allocate memory.", + "Enum end guard. DO NO use this as an error." +}; + +const char *labcomm_error_get_str(enum labcomm_error error_id) +{ + const char *error_str = NULL; + // Check if this is a known error ID. + if (error_id >= LABCOMM_ERROR_ENUM_BEGIN_GUARD && error_id <= LABCOMM_ERROR_ENUM_END_GUARD) { + error_str = labcomm_error_strings[error_id]; + } + return error_str; +} + +void labcomm_decoder_register_new_datatype_handler(struct labcomm_decoder *d, labcomm_handle_new_datatype_callback on_new_datatype) +{ + d->on_new_datatype = on_new_datatype; +} + +int on_new_datatype(labcomm_decoder_t *d, labcomm_signature_t *sig) +{ + d->on_error(LABCOMM_ERROR_DEC_UNKNOWN_DATATYPE, 4, "%s(): unknown datatype '%s'\n", __FUNCTION__, sig->name); + return 0; +} + +void on_error_fprintf(enum labcomm_error error_id, size_t nbr_va_args, ...) +{ +#ifndef LABCOMM_NO_STDIO + const char *err_msg = labcomm_error_get_str(error_id); // The final string to print. + if (err_msg == NULL) { + err_msg = "Error with an unknown error ID occured."; + } + fprintf(stderr, "%s\n", err_msg); + + if (nbr_va_args > 0) { + va_list arg_pointer; + va_start(arg_pointer, nbr_va_args); + + fprintf(stderr, "%s\n", "Extra info {"); + char *print_format = va_arg(arg_pointer, char *); + vfprintf(stderr, print_format, arg_pointer); + fprintf(stderr, "}\n"); + + va_end(arg_pointer); + } +#else + ; // If labcomm can't be compiled with stdio the user will have to make an own error callback functionif he/she needs error reporting. +#endif +} + + static labcomm_sample_entry_t *get_sample_by_signature_address( labcomm_sample_entry_t *head, labcomm_signature_t *signature) @@ -135,7 +226,7 @@ static void do_encode( if (sample && sample->encode) { sample->encode(encoder, value); } else { - printf("Encoder has no registration for %s\n", signature->name); + encoder->on_error(LABCOMM_ERROR_ENC_NO_REG_SIGNATURE, 2, "No registration for %s.\n", signature->name); } } @@ -148,7 +239,7 @@ labcomm_encoder_t *labcomm_encoder_new( labcomm_encoder_context_t *context; context = malloc(sizeof(labcomm_encoder_context_t)); - context->sample = 0; + context->sample = NULL; context->index = LABCOMM_USER; result->context = context; result->writer.context = writer_context; @@ -158,8 +249,10 @@ labcomm_encoder_t *labcomm_encoder_new( result->writer.pos = 0; result->writer.write = writer; result->writer.write(&result->writer, labcomm_writer_alloc); + result->writer.on_error = on_error_fprintf; result->do_register = do_encoder_register; result->do_encode = do_encode; + result->on_error = on_error_fprintf; } return result; } @@ -169,10 +262,11 @@ void labcomm_internal_encoder_register( labcomm_signature_t *signature, labcomm_encode_typecast_t encode) { - if (e && e->do_register) { + // Will segfault if e == NULL. + if (e->do_register) { e->do_register(e, signature, encode); } else { - printf("Encoder is missing do_register\n"); + e->on_error(LABCOMM_ERROR_ENC_MISSING_DO_REG, 0); } } @@ -181,10 +275,11 @@ void labcomm_internal_encode( labcomm_signature_t *signature, void *value) { - if (e && e->do_encode) { + // Will segfault if e == NULL + if (e->do_encode) { e->do_encode(e, signature, value); } else { - printf("Encoder is missing do_encode\n"); + e->on_error(LABCOMM_ERROR_ENC_MISSING_DO_ENCODE, 0); } } @@ -196,8 +291,18 @@ void labcomm_internal_encoder_user_action(labcomm_encoder_t *e, void labcomm_encoder_free(labcomm_encoder_t* e) { - e->writer.write(&e->writer, labcomm_writer_free); + labcomm_encoder_context_t *econtext = (labcomm_encoder_context_t *) e->context; + + labcomm_sample_entry_t *sentry = econtext->sample; + labcomm_sample_entry_t *sentry_next; + while (sentry != NULL) { + sentry_next = sentry->next; + free(sentry); + sentry = sentry_next; + } + + free(e->context); free(e); } @@ -251,7 +356,7 @@ static void collect_flat_signature( { int type = labcomm_decode_int(decoder); if (type >= LABCOMM_USER) { - printf("Implement %s ...\n", __FUNCTION__); + decoder->on_error(LABCOMM_ERROR_UNIMPLEMENTED_FUNC, 2, "Implement %s\n", __FUNCTION__); } else { labcomm_encode_int(signature_writer, type); switch (type) { @@ -287,7 +392,7 @@ static void collect_flat_signature( case LABCOMM_STRING: { } break; default: { - printf("Implement %s ...\n", __FUNCTION__); + decoder->on_error(LABCOMM_ERROR_UNIMPLEMENTED_FUNC, 2, "Implement %s\n", __FUNCTION__); } break; } } @@ -343,12 +448,12 @@ static int do_decode_one(labcomm_decoder_t *d) entry = get_sample_by_signature_value(context->sample, &signature); if (! entry) { // Unknown datatype, bail out - fprintf(stderr, "%s: unknown datatype '%s' (id=0x%x)\n", - __FUNCTION__, signature.name, index); + /*d->on_error(LABCOMM_ERROR_DEC_UNKNOWN_DATATYPE, 4, "%s(): unknown datatype '%s' (id=0x%x)\n", __FUNCTION__, signature.name, index);*/ + d->on_new_datatype(d, &signature); } else if (entry->index && entry->index != index) { - fprintf(stderr, "%s: index mismatch '%s' (id=0x%x != 0x%x)\n", - __FUNCTION__, signature.name, entry->index, index); + d->on_error(LABCOMM_ERROR_DEC_INDEX_MISMATCH, 5, "%s(): index mismatch '%s' (id=0x%x != 0x%x)\n", __FUNCTION__, signature.name, entry->index, index); } else { + // TODO unnessesary, since entry->index == index in above if statement entry->index = index; } free(signature.name); @@ -363,8 +468,9 @@ static int do_decode_one(labcomm_decoder_t *d) entry = get_sample_by_index(context->sample, result); if (!entry) { - fprintf(stderr, "%s: type not found (id=0x%x)\n", - __FUNCTION__, result); + // printf("Error: %s: type not found (id=0x%x)\n", + //__FUNCTION__, result); + d->on_error(LABCOMM_ERROR_DEC_TYPE_NOT_FOUND, 3, "%s(): type not found (id=0x%x)\n", __FUNCTION__, result); result = -ENOENT; } else { entry->decoder(d, entry->handler, entry->context); @@ -393,8 +499,11 @@ labcomm_decoder_t *labcomm_decoder_new( result->reader.pos = 0; result->reader.read = reader; result->reader.read(&result->reader, labcomm_reader_alloc); + result->reader.on_error = on_error_fprintf; result->do_register = do_decoder_register; result->do_decode_one = do_decode_one; + result->on_error = on_error_fprintf; + result->on_new_datatype = on_new_datatype; } return result; } @@ -406,23 +515,25 @@ void labcomm_internal_decoder_register( labcomm_handler_typecast_t handler, void *handler_context) { - if (d && d->do_register) { + // Will segfault if d == NULL + if (d->do_register) { d->do_register(d, signature, type_decoder, handler, handler_context); } else { - printf("Decoder is missing do_register\n"); + d->on_error(LABCOMM_ERROR_DEC_MISSING_DO_REG, 0); } } int labcomm_decoder_decode_one(labcomm_decoder_t *d) { int result = -1; - if (d && d->do_decode_one) + // Will segfault if decoder == NULL. + if (d->do_decode_one) { result = d->do_decode_one(d); } else { - printf("Decoder is missing do_decode_one\n"); + d->on_error(LABCOMM_ERROR_DEC_MISSING_DO_DECODE_ONE, 0); } return result; } @@ -436,5 +547,16 @@ void labcomm_decoder_run(labcomm_decoder_t *d) void labcomm_decoder_free(labcomm_decoder_t* d) { d->reader.read(&d->reader, labcomm_reader_free); + labcomm_decoder_context_t *context = (labcomm_decoder_context_t *) d->context; + labcomm_sample_entry_t *entry = context->sample; + labcomm_sample_entry_t *entry_next; + + while (entry != NULL) { + entry_next = entry->next; + free(entry); + entry = entry_next; + } + + free(d->context); free(d); } diff --git a/lib/c/labcomm.h b/lib/c/labcomm.h index 9fed697..5763b8d 100644 --- a/lib/c/labcomm.h +++ b/lib/c/labcomm.h @@ -1,10 +1,24 @@ #ifndef _LABCOMM_H_ #define _LABCOMM_H_ -#include <endian.h> -#include <stdio.h> +#ifdef ARM_CORTEXM3_CODESOURCERY + #include <machine/endian.h> +#else + #include <endian.h> +#endif + +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif + #include <stdlib.h> #include <string.h> +#include <stdarg.h> + +/* Forward declaration */ +struct labcomm_encoder; +struct labcomm_decoder; /* * Signature entry @@ -17,11 +31,62 @@ typedef struct { unsigned char *signature; } labcomm_signature_t; +/* + * Error handling. + */ + +/* Error IDs */ +enum labcomm_error { + LABCOMM_ERROR_ENUM_BEGIN_GUARD, // _must_ be the first enum element. labcomm_error_get_str() depends on this. + LABCOMM_ERROR_ENC_NO_REG_SIGNATURE, + LABCOMM_ERROR_ENC_MISSING_DO_REG, + LABCOMM_ERROR_ENC_MISSING_DO_ENCODE, + LABCOMM_ERROR_ENC_BUF_FULL, + LABCOMM_ERROR_DEC_MISSING_DO_REG, + LABCOMM_ERROR_DEC_MISSING_DO_DECODE_ONE, + LABCOMM_ERROR_DEC_UNKNOWN_DATATYPE, + LABCOMM_ERROR_DEC_INDEX_MISMATCH, + LABCOMM_ERROR_DEC_TYPE_NOT_FOUND, + LABCOMM_ERROR_UNIMPLEMENTED_FUNC, + LABCOMM_ERROR_MEMORY, + LABCOMM_ERROR_USER_DEF, + LABCOMM_ERROR_ENUM_END_GUARD // _must_ be the last enum element. labcomm_error_get_str() depends on this. +}; + +/* Error strings. _must_ be the same order as in enum labcomm_error */ +extern const char *labcomm_error_strings[]; + +/* The callback prototype for error handling.\ + * First parameter is the error ID. + * The second paramters is the number of va_args that comes after this one. If noneit should be 0. + * Optionaly other paramters can be supplied depending on what is needed for this error ID. + */ +typedef void (* labcomm_error_handler_callback)(enum labcomm_error error_id, size_t nbr_va_args, ...); + +/* Default error handler, prints message to stderr. + * Extra info about the error can be supplied as char* as VA-args. Especially user defined errors should supply a describing string. if nbr_va_args > 1 the first variable argument must be a printf format string and the possibly following arguments are passed as va_args to vprintf. + */ +void on_error_fprintf(enum labcomm_error error_id, size_t nbr_va_args, ...); + +/* Register a callback for the error handler for this encoder. */ +void labcomm_register_error_handler_encoder(struct labcomm_encoder *encoder, labcomm_error_handler_callback callback); + +/* Register a callback for the error handler for this decoder. */ +void labcomm_register_error_handler_decoder(struct labcomm_decoder *decoder, labcomm_error_handler_callback callback); + +/* Get a string describing the supplied standrad labcomm error. */ +const char *labcomm_error_get_str(enum labcomm_error error_id); + +typedef int (* labcomm_handle_new_datatype_callback)(struct labcomm_decoder *decoder, + labcomm_signature_t *sig); + +void labcomm_decoder_register_new_datatype_handler(struct labcomm_decoder *d, + labcomm_handle_new_datatype_callback on_new_datatype); + /* * Decoder */ -struct labcomm_decoder; typedef enum { labcomm_reader_alloc, @@ -38,6 +103,7 @@ typedef struct labcomm_reader { int count; int pos; int (*read)(struct labcomm_reader *, labcomm_reader_action_t); + labcomm_error_handler_callback on_error; } labcomm_reader_t; struct labcomm_decoder *labcomm_decoder_new( @@ -53,7 +119,6 @@ void labcomm_decoder_free( /* * Encoder */ -struct labcomm_encoder; typedef enum { labcomm_writer_alloc, @@ -71,9 +136,9 @@ typedef struct labcomm_writer { int count; int pos; int (*write)(struct labcomm_writer *, labcomm_writer_action_t); + labcomm_error_handler_callback on_error; } labcomm_writer_t; -struct labcomm_encoder; struct labcomm_encoder *labcomm_encoder_new( int (*writer)(labcomm_writer_t *, labcomm_writer_action_t), void *writer_context); diff --git a/lib/c/labcomm_mem_reader.c b/lib/c/labcomm_mem_reader.c new file mode 100644 index 0000000..088123d --- /dev/null +++ b/lib/c/labcomm_mem_reader.c @@ -0,0 +1,73 @@ +#include "labcomm_mem_reader.h" + +#include <errno.h> + +/* This implementation assumes labcomm will call end exactly once after each start + * It is not allowed to save data in mcontext->enc_data, + * this pointer will be set to NULL after decoding. + */ +/* NOTE!!!! + * start will be run first, once a signature or a data section is decoded + * end will be run and then start again. If end of encoded data is reached this + * must be handled in start. + */ + +// TODO make labcomm use result! +int labcomm_mem_reader(labcomm_reader_t *r, labcomm_reader_action_t action) +{ + int result = -EINVAL; + labcomm_mem_reader_context_t *mcontext = (labcomm_mem_reader_context_t *) r->context; + + switch (action) { + case labcomm_reader_alloc: { + r->data = NULL; + r->data_size = 0; + r->pos = 0; + r->count = 0; + } break; + case labcomm_reader_start: { + if (r->data == NULL && mcontext->enc_data != NULL) { + r->data = (unsigned char *) malloc(mcontext->size); + if(r->data != NULL) { + memcpy(r->data, mcontext->enc_data, mcontext->size); + r->data_size = mcontext->size; + r->count = mcontext->size; + r->pos = 0; + result = r->data_size; + } else { + r->data_size = 0; + result = -ENOMEM; + } + } else if (r->data == NULL && mcontext->enc_data == NULL) { + result = -1; + } else { + result = r->count - r->pos; + } + } break; + case labcomm_reader_continue: { + if (r->pos < r->count) { + result = r->count - r->pos; + } else { + // TODO set some describing error here + result = -1; + } + } break; + case labcomm_reader_end: { + if (r->pos >= r->count) { + free(r->data); + r->data = NULL; + r->data_size = 0; + mcontext->enc_data = NULL; + mcontext->size = 0; + } + result = r->count - r->pos; + } break; + case labcomm_reader_free: { + r->count = 0; + r->pos = 0; + result = 0; + } break; + } + return result; +} + diff --git a/lib/c/labcomm_mem_reader.h b/lib/c/labcomm_mem_reader.h new file mode 100644 index 0000000..5cac018 --- /dev/null +++ b/lib/c/labcomm_mem_reader.h @@ -0,0 +1,17 @@ +#ifndef LABCOMM_MEM_READER_H +#define LABCOMM_MEM_READER_H + +#include "labcomm.h" + +/* enc_data: The data to be decoded + * size: the size of the data to be decoded + */ +typedef struct labcomm_mem_reader_context_t labcomm_mem_reader_context_t; +struct labcomm_mem_reader_context_t { + size_t size; + unsigned char *enc_data; +}; + +int labcomm_mem_reader( labcomm_reader_t *r, labcomm_reader_action_t action); + +#endif diff --git a/lib/c/labcomm_mem_writer.c b/lib/c/labcomm_mem_writer.c new file mode 100644 index 0000000..7275649 --- /dev/null +++ b/lib/c/labcomm_mem_writer.c @@ -0,0 +1,143 @@ +#include "labcomm_mem_writer.h" + +#include "stddef.h" // For size_t. +#include <errno.h> + +#include "labcomm.h" +#include "cppmacros.h" + +#define BUFFER_SIZE 150 // Suitable size is at least the size of a fully encoded message. Found by inspecting size of file genreated from the labcomm_fs_reader_writer.c on the same message type. + +// Put encdoded data directly in mcontext->mbuf or malloc new temporary memory. +// 1 == Allocate new memory. +// 2 == Use mcontext->buf directly. But _beware_; you can not then later change +// mcontext->buf to something else since the writer gets a reference to this +// buffer! +#if defined(MEM_WRITER_ENCODED_BUFFER) && (EMPTY(MEM_WRITER_ENCODED_BUFFER) != 1) + #define ENCODED_BUFFER MEM_WRITER_ENCODED_BUFFER +#else + #define ENCODED_BUFFER 1 +#endif + +static int get_writer_available(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext); +static void copy_data(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext, unsigned char *mbuf); + +/* + * Write encoded messages to memory. w->context is assumed to be a pointer to a + * labcomm_mem_writer_context_t structure. + */ +int labcomm_mem_writer(labcomm_writer_t *w, labcomm_writer_action_t action) +{ + int result = 0; + // Unwrap pointers for easy access. + labcomm_mem_writer_context_t *mcontext = (labcomm_mem_writer_context_t *) w->context; + unsigned char *mbuf = mcontext->buf; + + switch (action) { + case labcomm_writer_alloc: { +#if (ENCODED_BUFFER == 1) + w->data = malloc(BUFFER_SIZE); // Buffer that LabComm will use for putting the encoded data. + if (w->data == NULL) { + result = -ENOMEM; + w->data_size = 0; + w->count = 0; + w->pos = 0; + } else { + w->data_size = BUFFER_SIZE; + w->count = BUFFER_SIZE; + w->pos = 0; + } +#elif (ENCODED_BUFFER == 2) + w->data = mbuf; + int bytes_left = (mcontext->length - mcontext->write_pos); + w->data_size = bytes_left; + w->count = bytes_left; + w->pos = mcontext->write_pos; +#endif + } break; + case labcomm_writer_free:{ +#if (ENCODED_BUFFER == 1) + free(w->data); +#endif + w->data = 0; + w->data_size = 0; + w->count = 0; + w->pos = 0; + } break; + case labcomm_writer_start:{ +#if (ENCODED_BUFFER == 1) + w->pos = 0; +#elif (ENCODED_BUFFER == 2) + w->pos = mcontext->write_pos; +#endif + } break; + case labcomm_writer_continue:{ // Encode-buffer(w->data) is full; empty/handle it. (w->pos == w->count) most likely. +#if (ENCODED_BUFFER == 1) + copy_data(w, mcontext, mbuf); + result = w->pos; // Assume result here should be number of bytes written. + w->pos = 0; +#elif (ENCODED_BUFFER == 2) + mcontext->write_pos = w->pos; +#endif + result = 0; + } break; + case labcomm_writer_end:{ // Nothing more to encode, handle encode-buffer(w->data). +#if (ENCODED_BUFFER == 1) + copy_data(w, mcontext, mbuf); + result = w->pos; + w->pos = 0; +#elif (ENCODED_BUFFER == 2) + mcontext->write_pos = w->pos; +#endif + result = 0; + } break; + case labcomm_writer_available:{ + result = w->count - w->pos; + } break; + } + return result; +} + +labcomm_mem_writer_context_t *labcomm_mem_writer_context_t_new(size_t init_pos, size_t length, unsigned char *buf) +{ + labcomm_mem_writer_context_t *mcontext = (labcomm_mem_writer_context_t *) malloc(sizeof(labcomm_mem_writer_context_t)); + if (mcontext == NULL) { + //fprintf(stderr, "error: Can not allocate labcomm_mem_writer_context_t.\n"); + } else { + mcontext->write_pos = init_pos; + mcontext->length = length; + mcontext->buf = buf; + } + return mcontext; +} + +void labcomm_mem_writer_context_t_free(labcomm_mem_writer_context_t **mcontext) +{ + free(*mcontext); + *mcontext = NULL; +} + +// Get the number of available bytes in the mcontext->buf buffer. +static int get_writer_available(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext) +{ + return (mcontext->length - mcontext->write_pos); +} + +// Copy data from encoded buffer to mbuf. +static void copy_data(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext, unsigned char *mbuf) +{ + int writer_available = get_writer_available(w, mcontext); + if (( writer_available - w->pos) < 0) { + w->on_error(LABCOMM_ERROR_ENC_BUF_FULL, 3, "labcomm_writer_t->pos=%i, but available in mcontext is %i", w->pos, writer_available); + } else { + int i; + for (i = 0; i < w->pos; ++i, mcontext->write_pos++) { + mbuf[mcontext->write_pos] = w->data[i]; + } + } +} + +void test_copy_data(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext, unsigned char *mbuf) +{ + copy_data(w, mcontext, mbuf); +} diff --git a/lib/c/labcomm_mem_writer.h b/lib/c/labcomm_mem_writer.h new file mode 100644 index 0000000..4585903 --- /dev/null +++ b/lib/c/labcomm_mem_writer.h @@ -0,0 +1,25 @@ +#ifndef LABCOMM_MEM_WRITER_H +#define LABCOMM_MEM_WRITER_H + +#include "labcomm.h" + +/* Wrapper structure for the memory buffer including a writer position. */ +typedef struct labcomm_mem_writer_context_t labcomm_mem_writer_context_t; +struct labcomm_mem_writer_context_t { + size_t write_pos; // Position where next write should be. + size_t length; // Length of the buffer. + unsigned char *buf; // Allocated destination buffer. +}; + +int labcomm_mem_writer(labcomm_writer_t *w, labcomm_writer_action_t action); + +/* Wrapper the internal static function copy_data. This is needed so that the exceptions can be unit tested. */ +void test_copy_data(labcomm_writer_t *w, labcomm_mem_writer_context_t *mcontext, unsigned char *mbuf); + +/* Allocate new labcomm_mem_writer_context_t. */ +labcomm_mem_writer_context_t *labcomm_mem_writer_context_t_new(size_t init_pos, size_t length, unsigned char *buf); + +/* Deallocate mcontext. */ +void labcomm_mem_writer_context_t_free(labcomm_mem_writer_context_t **mcontext); + +#endif diff --git a/lib/c/labcomm_private.h b/lib/c/labcomm_private.h index 3bc5140..bd5dcfa 100644 --- a/lib/c/labcomm_private.h +++ b/lib/c/labcomm_private.h @@ -1,8 +1,17 @@ #ifndef _LABCOMM_PRIVATE_H_ #define _LABCOMM_PRIVATE_H_ -#include <endian.h> -#include <stdio.h> +#ifdef ARM_CORTEXM3_CODESOURCERY + #include <machine/endian.h> +#else + #include <endian.h> +#endif + +// Some projects can not use stdio.h. +#ifndef LABCOMM_NO_STDIO + #include <stdio.h> +#endif + #include <stdlib.h> #include <string.h> #include "labcomm.h" @@ -51,6 +60,8 @@ typedef struct labcomm_decoder { labcomm_handler_typecast_t, void *context); int (*do_decode_one)(struct labcomm_decoder *decoder); + labcomm_error_handler_callback on_error; + labcomm_handle_new_datatype_callback on_new_datatype; } labcomm_decoder_t; /* @@ -176,6 +187,7 @@ typedef struct labcomm_encoder { void (*do_encode)(struct labcomm_encoder *encoder, labcomm_signature_t *signature, void *value); + labcomm_error_handler_callback on_error; } labcomm_encoder_t; void labcomm_internal_encoder_register( @@ -198,7 +210,7 @@ void labcomm_internal_encoder_user_action(struct labcomm_encoder *encoder, static inline void labcomm_write_##name(labcomm_writer_t *w, type data) { \ int i; \ for (i = sizeof(type) - 1 ; i >= 0 ; i--) { \ - if (w->pos >= w->count) { \ + if (w->pos >= w->count) { /*buffer is full*/ \ w->write(w, labcomm_writer_continue); \ } \ w->data[w->pos] = ((unsigned char*)(&data))[i]; \ diff --git a/lib/c/test/test_labcomm.c b/lib/c/test/test_labcomm.c new file mode 100644 index 0000000..fc4004c --- /dev/null +++ b/lib/c/test/test_labcomm.c @@ -0,0 +1,187 @@ + +#include "CUnit/Basic.h" +#include "CUnit/Console.h" +#include <stdbool.h> + +#include <labcomm.h> +#include <labcomm_mem_writer.h> +#include <labcomm_mem_reader.h> +#include "test/testdata/gen/test_sample.h" + +#define TEST_BUFFER_SIZE (50) + +void test_error_handler(enum labcomm_error error_id, size_t nbr_va_args, ...); + +int init_suit_labcomm() +{ + return 0; +} + +int clean_suit_labcomm() +{ + return 0; +} + +void setup_connected_encoder_decoder(struct labcomm_encoder **enc, + labcomm_mem_writer_context_t *enc_ctx, + struct labcomm_decoder **dec, + labcomm_mem_reader_context_t *dec_ctx) +{ + enc_ctx->write_pos = 0; + enc_ctx->buf = malloc(TEST_BUFFER_SIZE); + enc_ctx->length = TEST_BUFFER_SIZE; + + *enc = labcomm_encoder_new(labcomm_mem_writer, enc_ctx); + + dec_ctx->size = 0; + dec_ctx->enc_data = enc_ctx->buf; + *dec = labcomm_decoder_new(labcomm_mem_reader, dec_ctx); + + labcomm_register_error_handler_decoder(*dec, test_error_handler); + labcomm_register_error_handler_encoder(*enc, test_error_handler); +} + +static bool in_error = false; +static enum labcomm_error in_error_id = LABCOMM_ERROR_ENUM_BEGIN_GUARD; +void test_error_handler(enum labcomm_error error_id, size_t nbr_va_args, ...) +{ + in_error = true; + in_error_id = error_id; +} + +static bool got_sample = false; +void test_decoder_handle_test_sample_test_var(test_sample_test_var *v, void *ctx) +{ + got_sample = true; +} + +void test_decoder_decode_sig() +{ + labcomm_mem_writer_context_t enc_ctx; + struct labcomm_encoder *encoder; + labcomm_mem_reader_context_t dec_ctx; + struct labcomm_decoder *decoder; + setup_connected_encoder_decoder(&encoder, &enc_ctx, &decoder, &dec_ctx); + + labcomm_encoder_register_test_sample_test_var(encoder); + dec_ctx.size = enc_ctx.write_pos; + + labcomm_decoder_register_test_sample_test_var(decoder, + test_decoder_handle_test_sample_test_var, NULL); + labcomm_decoder_decode_one(decoder); + + CU_ASSERT_FALSE(in_error); + enc_ctx.write_pos = 0; + test_sample_test_var var = 1; + labcomm_encode_test_sample_test_var(encoder, &var); + dec_ctx.size = enc_ctx.write_pos; + labcomm_decoder_decode_one(decoder); + + CU_ASSERT_FALSE(in_error); + CU_ASSERT_FALSE(got_sample); + + labcomm_decoder_free(decoder); + labcomm_encoder_free(encoder); + free(enc_ctx.buf); + + in_error = false; + in_error_id = LABCOMM_ERROR_ENUM_BEGIN_GUARD; + got_sample = false; +} + +static bool got_new_datatype = false; +static labcomm_signature_t new_sig; +int test_new_datatype(struct labcomm_decoder *decoder, + labcomm_signature_t *sig) +{ + got_new_datatype = true; + memcpy(&new_sig, sig, sizeof(labcomm_signature_t)); + return 0; +} + +void test_decode_unreg_signature_handle() +{ + labcomm_mem_writer_context_t enc_ctx; + struct labcomm_encoder *encoder; + labcomm_mem_reader_context_t dec_ctx; + struct labcomm_decoder *decoder; + setup_connected_encoder_decoder(&encoder, &enc_ctx, &decoder, &dec_ctx); + + labcomm_encoder_register_test_sample_test_var(encoder); + dec_ctx.size = enc_ctx.write_pos; + labcomm_decoder_register_new_datatype_handler(decoder, test_new_datatype); + labcomm_decoder_decode_one(decoder); + + CU_ASSERT_TRUE(got_new_datatype); + CU_ASSERT_EQUAL( + memcmp(new_sig.signature, dec_ctx.enc_data, dec_ctx.size), 0); + + got_new_datatype = false; + labcomm_decoder_free(decoder); + labcomm_encoder_free(encoder); + free(enc_ctx.buf); +} + +void test_decode_unreg_signature_error() +{ + labcomm_mem_writer_context_t enc_ctx; + struct labcomm_encoder *encoder; + labcomm_mem_reader_context_t dec_ctx; + struct labcomm_decoder *decoder; + setup_connected_encoder_decoder(&encoder, &enc_ctx, &decoder, &dec_ctx); + + labcomm_encoder_register_test_sample_test_var(encoder); + dec_ctx.size = enc_ctx.write_pos; + + labcomm_decoder_decode_one(decoder); + + CU_ASSERT_TRUE(in_error); + CU_ASSERT_EQUAL(in_error_id, LABCOMM_ERROR_DEC_UNKNOWN_DATATYPE); + got_new_datatype = false; + labcomm_decoder_free(decoder); + labcomm_encoder_free(encoder); + free(enc_ctx.buf); +} +int main() +{ + CU_pSuite suite_decoder = NULL; + + // Initialize CUnit test registry. + if (CUE_SUCCESS != CU_initialize_registry()) { + return CU_get_error(); + } + + // Add our test suites. + suite_decoder = CU_add_suite("transport_enc_dec", + init_suit_labcomm, clean_suit_labcomm); + if (suite_decoder == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + (CU_add_test(suite_decoder, "test_decoder_decode_sig", + test_decoder_decode_sig) == NULL) + || + (CU_add_test(suite_decoder, "test_decode_unreg_signature_handle", + test_decode_unreg_signature_handle) == NULL) + || + (CU_add_test(suite_decoder, "test_decode_unreg_signature_error", + test_decode_unreg_signature_error) == NULL) + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + // Set verbosity. + CU_basic_set_mode(CU_BRM_VERBOSE); + /*CU_console_run_tests();*/ + + // Run all test suites. + CU_basic_run_tests(); + + // Clean up. + CU_cleanup_registry(); + + return CU_get_error(); +} diff --git a/lib/c/test/test_labcomm_errors b/lib/c/test/test_labcomm_errors new file mode 100755 index 0000000000000000000000000000000000000000..061b60594868d10026d20b7d650a188827d456ab GIT binary patch literal 46272 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�^Ls)P|I}^8Y_*>Jg+CghAp{ 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 literal 0 HcmV?d00001 diff --git a/lib/c/test/test_labcomm_errors.c b/lib/c/test/test_labcomm_errors.c new file mode 100644 index 0000000..7742175 --- /dev/null +++ b/lib/c/test/test_labcomm_errors.c @@ -0,0 +1,168 @@ +#include "test_labcomm_errors.h" + +#include <stdlib.h> + +#include <labcomm.h> +#include <labcomm_private.h> +#include <labcomm_mem_writer.h> +#include <labcomm_mem_reader.h> + +static enum labcomm_error callback_error_id; + +int assert_callback(enum labcomm_error expected, const char *name, const char *err_msg) +{ + int success; + printf("----> %s()\n", name); + if (callback_error_id == expected) { + printf("Succeeded.\n"); + success = 1; + } else { + printf("Failed! %s\n", err_msg); + success = 0; + } + return success; +} + +/* Our callback that just logs which error_id that the library reported. */ +void test_callback(enum labcomm_error error_id, size_t nbr_va_args, ...) +{ + va_list arg_pointer; + va_start(arg_pointer, nbr_va_args); + va_end(arg_pointer); + callback_error_id = error_id; +} + +void reset_callback_erro_id() +{ + callback_error_id = -128; +} + +int encoded_size_mock(void *voidp) +{ + return 0; +} + +int test_enc_not_reg_encoder_sign() +{ + reset_callback_erro_id(); + unsigned char *buf = (unsigned char *) "a"; + labcomm_mem_writer_context_t *mcontext = labcomm_mem_writer_context_t_new(0, 1, buf); + labcomm_encoder_t *encoder = labcomm_encoder_new(labcomm_mem_writer, mcontext); + labcomm_register_error_handler_encoder(encoder, test_callback); + + labcomm_signature_t signature = {.type = 0, .name = "test_signature", .encoded_size = encoded_size_mock, .size = 0, .signature = (unsigned char *) "0"}; + encoder->do_encode(encoder, &signature, NULL); + + return assert_callback(LABCOMM_ERROR_ENC_NO_REG_SIGNATURE, __FUNCTION__, ""); +} + +int test_enc_missing_do_reg() +{ + reset_callback_erro_id(); + unsigned char *buf = (unsigned char *) "a"; + labcomm_mem_writer_context_t *mcontext = labcomm_mem_writer_context_t_new(0, 1, buf); + labcomm_encoder_t *encoder = labcomm_encoder_new(labcomm_mem_writer, mcontext); + labcomm_register_error_handler_encoder(encoder, test_callback); + + encoder->do_register = NULL; + labcomm_internal_encoder_register(encoder, NULL, NULL); + + return assert_callback(LABCOMM_ERROR_ENC_MISSING_DO_REG, __FUNCTION__, ""); +} + +int test_enc_missing_do_encode() +{ + reset_callback_erro_id(); + unsigned char *buf = (unsigned char *) "a"; + labcomm_mem_writer_context_t *mcontext = labcomm_mem_writer_context_t_new(0, 1, buf); + labcomm_encoder_t *encoder = labcomm_encoder_new(labcomm_mem_writer, mcontext); + labcomm_register_error_handler_encoder(encoder, test_callback); + + encoder->do_encode = NULL; + labcomm_internal_encode(encoder, NULL, NULL); + + return assert_callback(LABCOMM_ERROR_ENC_MISSING_DO_ENCODE, __FUNCTION__, ""); +} + +int test_enc_buf_full() +{ + reset_callback_erro_id(); + unsigned char *buf = (unsigned char *) "a"; + labcomm_mem_writer_context_t *mcontext = labcomm_mem_writer_context_t_new(0, 1, buf); + labcomm_encoder_t *encoder = labcomm_encoder_new(labcomm_mem_writer, mcontext); + labcomm_register_error_handler_encoder(encoder, test_callback); + + unsigned char *mbuf = mcontext->buf; + labcomm_writer_t writer = encoder->writer; + writer.data = malloc(1); + writer.pos = 1; + mcontext->write_pos = 1; + test_copy_data(&writer, mcontext, mbuf); + + return assert_callback(LABCOMM_ERROR_ENC_BUF_FULL, __FUNCTION__, ""); +} + +void labcomm_decoder_typecast_t_mock(struct labcomm_decoder *decoder, labcomm_handler_typecast_t handler, void *voidp) +{ + ; +} + +void labcomm_handler_typecast_t_mock(void *arg1, void *arg2) +{ + ; +} + +int test_dec_missing_do_reg() +{ + reset_callback_erro_id(); + unsigned char *buf = (unsigned char *) "a"; + labcomm_mem_reader_context_t *mcontext = (labcomm_mem_reader_context_t *) malloc(sizeof(labcomm_mem_reader_context_t)); + labcomm_decoder_t *decoder = labcomm_decoder_new(labcomm_mem_reader, mcontext); + labcomm_register_error_handler_decoder(decoder, test_callback); + + decoder->do_register = NULL; + labcomm_internal_decoder_register(decoder, NULL, labcomm_decoder_typecast_t_mock, labcomm_handler_typecast_t_mock, buf); + + return assert_callback(LABCOMM_ERROR_DEC_MISSING_DO_REG, __FUNCTION__, ""); +} + +int test_dec_missing_do_decode_one() +{ + reset_callback_erro_id(); + labcomm_mem_reader_context_t *mcontext = (labcomm_mem_reader_context_t *) malloc(sizeof(labcomm_mem_reader_context_t)); + labcomm_decoder_t *decoder = labcomm_decoder_new(labcomm_mem_reader, mcontext); + labcomm_register_error_handler_decoder(decoder, test_callback); + + decoder->do_decode_one = NULL; + labcomm_decoder_decode_one(decoder); + + return assert_callback(LABCOMM_ERROR_DEC_MISSING_DO_DECODE_ONE, __FUNCTION__, ""); +} + +int main() +{ + printf("####> Begin tests.\n"); + size_t nbr_succeed = 0; + size_t nbr_tests = 6; // Increment this when new tests are written. + nbr_succeed += test_enc_not_reg_encoder_sign(); + nbr_succeed += test_enc_missing_do_reg(); + nbr_succeed += test_enc_missing_do_encode(); + nbr_succeed += test_enc_buf_full(); + nbr_succeed += test_dec_missing_do_reg(); + nbr_succeed += test_dec_missing_do_decode_one(); + + // Too tedius to test really... + //nbr_succeed += test_dec_unknown_datatype(); + //nbr_succeed += test_dec_index_mismatch(); + //nbr_succeed += test_dec_type_not_found(); + + //nbr_succeed += test_unimplemented_func(); // This test will be obsolete in the future ;-) + //nbr_succeed += test_user_def(); // There are no user defined errors in the library of course. + + printf("####> End tests.\nSummary: %i/%i tests succeed.\n", nbr_succeed, nbr_tests); + if (nbr_succeed == nbr_tests) { + return EXIT_SUCCESS; + } else { + return EXIT_FAILURE; + } +} diff --git a/lib/c/test/test_labcomm_errors.h b/lib/c/test/test_labcomm_errors.h new file mode 100644 index 0000000..8cc3918 --- /dev/null +++ b/lib/c/test/test_labcomm_errors.h @@ -0,0 +1,6 @@ +#ifndef TEST_LABCOMM_ERRORS_H +#define TEST_LABCOMM_ERRORS_H + +void test_not_reg_encoder_sign(); + +#endif diff --git a/lib/c/test/testdata/test_sample.lc b/lib/c/test/testdata/test_sample.lc new file mode 100644 index 0000000..bf686a7 --- /dev/null +++ b/lib/c/test/testdata/test_sample.lc @@ -0,0 +1 @@ +sample int test_var; -- GitLab