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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 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