From 091e6bf4b9213884c8dc81510af9cb9da2cc8990 Mon Sep 17 00:00:00 2001
From: Johannes Schriewer <hallo@dunkelstern.de>
Date: Sun, 29 Jul 2018 03:41:55 +0200
Subject: [PATCH] General cleanup

---
 .gitignore            |  6 +++++
 .vscode/settings.json |  4 ++++
 Makefile.linux        | 32 ++++++++++++++++++++-----
 src/debug.h           |  4 +++-
 src/mqtt.h            | 21 ++++++++++------
 src/packet.c          | 56 ++++++++++++++++++++++++++++++++++---------
 tests/Makefile        | 18 ++++++++------
 tests/decode_packet.c | 23 ++++++++++++------
 tests/encode_packet.c | 10 ++------
 tests/test.h          |  7 ++++++
 10 files changed, 134 insertions(+), 47 deletions(-)

diff --git a/.gitignore b/.gitignore
index e0292b1..f6aa3a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,8 @@
 *.o
 *.a
+*.do
+*.gcno
+*.gcda
+*.gcov
+coverage/
+*.test
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d086cad..937ca70 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -22,6 +22,10 @@
         "**/CVS": true,
         "**/.DS_Store": true,
         "**/*.o": true,
+        "**/*.do": true,
+        "**/*.gcno": true,
+        "**/*.gcda": true,
+        "**/*.gcov": true,
         "**/*.a": true
     }
 }
diff --git a/Makefile.linux b/Makefile.linux
index 15eefab..0bd782a 100644
--- a/Makefile.linux
+++ b/Makefile.linux
@@ -1,22 +1,39 @@
 SRCS=src/mqtt.c src/packet.c src/protocol.c src/debug.c platform/linux.c
 OBJS=$(SRCS:%.c=%.o)
+DEBUG_OBJS=$(SRCS:%.c=%.do)
+COVERAGE_FILES=$(SRCS:%.c=%.gcno) $(SRCS:%.c=%.gcda)
 
 TARGET=libmqtt.a
+DEBUG_TARGET=libmqtt-debug.a
 
-PLATFORM_FLAGS=-DMAX_BUFFER_SIZE=256 -DDEBUG=1
+PLATFORM_FLAGS=
 
 AR=ar
-CC=clang
+CC=gcc
 CFLAGS=-g -Os -Wall -pthread -I./platform -I./src $(PLATFORM_FLAGS)
+COVERAGE_FLAGS=-fprofile-arcs -ftest-coverage
+DEBUG_FLAGS=-DDEBUG=1 $(COVERAGE_FLAGS)
 
-all: $(TARGET)
+all: $(TARGET) $(DEBUG_TARGET)
 
-test: $(TARGET)
+test: $(DEBUG_TARGET)
 	$(MAKE) -C tests
 
+coverage: test
+	lcov --capture --directory . --output-file lcov.info
+	rm -rf coverage
+	mkdir -p coverage
+	genhtml -o coverage lcov.info
+
 $(TARGET): $(OBJS)
 	$(AR) -cr $(TARGET) $(OBJS)
 
+$(DEBUG_TARGET): $(DEBUG_OBJS)
+	$(AR) -cr $(DEBUG_TARGET) $(DEBUG_OBJS)
+
+%.do: %.c
+	$(CC) $(CFLAGS) $(DEBUG_FLAGS) -o $@ -c $<
+
 %.o: %.c
 	$(CC) $(CFLAGS) -o $@ -c $<
 
@@ -24,6 +41,9 @@ mqtt.o: mqtt.h
 
 clean:
 	$(MAKE) -C tests clean
-	rm -f $(TARGET)
-	rm -f $(OBJS)
+	rm -f $(TARGET) $(DEBUG_TARGET)
+	rm -f $(OBJS) $(DEBUG_OBJS)
+	rm -f $(COVERAGE_FILES)
+	rm -f *.gcov
+	rm -f lcov.info
 	rm -rf docs/
diff --git a/src/debug.h b/src/debug.h
index edbb7bb..bf0c477 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -6,8 +6,10 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdint.h>
+#include <string.h>
+#include <errno.h>
 
-#define DEBUG_LOG(fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__);
+#define DEBUG_LOG(fmt, ...) fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__);
 
 static inline void hexdump(char *data, size_t len, int indent) {
     for (int i = 0; i < len;) {
diff --git a/src/mqtt.h b/src/mqtt.h
index eadbfe8..51e536c 100644
--- a/src/mqtt.h
+++ b/src/mqtt.h
@@ -7,13 +7,13 @@
 typedef struct _MQTTHandle MQTTHandle;
 
 typedef struct {
-    char *hostname; /**< Hostname to connect to, will do DNS resolution */
-    uint16_t port;  /**< Port the broker listens on, set to 0 for 1883 default */
+    char *hostname;  /**< Hostname to connect to, will do DNS resolution */
+    uint16_t port;   /**< Port the broker listens on, set to 0 for 1883 default */
 
-    char *clientID; /**< Client identification */
+    char *client_id; /**< Client identification */
 
-    char *username; /**< User name, set to NULL to connect anonymously */
-    char *password; /**< Password, set to NULL to connect without password */
+    char *username;  /**< User name, set to NULL to connect anonymously */
+    char *password;  /**< Password, set to NULL to connect without password */
 } MQTTConfig;
 
 typedef enum {
@@ -36,8 +36,11 @@ typedef enum {
     MQTT_Error_Connection_Reset        /**< Network connection reset, perhaps network went down? */
 } MQTTErrorCode;
 
-/** Error handler callback */
-typedef void (*MQTTErrorHandler)(MQTTHandle *handle, MQTTErrorCode code);
+/** Error handler callback
+ * 
+ * Return true if the handle should be freed, false to keep it
+ */
+typedef bool (*MQTTErrorHandler)(MQTTHandle *handle, MQTTErrorCode code);
 
 /** Event handler callback */
 typedef void (*MQTTEventHandler)(MQTTHandle *handle, char *topic, char *payload);
@@ -48,6 +51,10 @@ typedef void (*MQTTEventHandler)(MQTTHandle *handle, char *topic, char *payload)
  * @param config: MQTT configuration
  * @param callback: Callback function to call on errors
  * @returns handle to mqtt connection or NULL on error
+ * 
+ * If the error handler is called with Host not found or Connection refused,
+ * the handler is in charge of freeing the handle by returning true
+ * or re-trying by changing settings and calling mqtt_reconnect() and returning false
  */
 MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTErrorHandler callback);
 
diff --git a/src/packet.c b/src/packet.c
index 76bc4bb..e27f660 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -168,7 +168,10 @@ bool decode_connect(Buffer *buffer, ConnectPayload *payload) {
     payload->protocol_level = buffer->data[buffer->position++];
     uint8_t flags = buffer->data[buffer->position++];
     payload->clean_session = ((flags & 0x02) > 0);
-    payload->keepalive_interval = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->keepalive_interval = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
     payload->client_id = utf8_string_decode(buffer);
     
     // last will
@@ -208,7 +211,10 @@ bool decode_publish(Buffer *buffer, PublishPayload *payload, size_t sz) {
     payload->duplicate = ((flags & 0x08) > 0);
 
     payload->topic = utf8_string_decode(buffer);
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id =
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
 
     size_t len = sz - (buffer->position - start_pos) + 1;
     if (len > 1) {
@@ -222,31 +228,46 @@ bool decode_publish(Buffer *buffer, PublishPayload *payload, size_t sz) {
 }
 
 bool decode_puback(Buffer *buffer, PubAckPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
-
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
     return true;
 }
 
 bool decode_pubrec(Buffer *buffer, PubRecPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
 
     return true;    
 }
 
 bool decode_pubrel(Buffer *buffer, PubRelPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
 
     return true;
 }
 
 bool decode_pubcomp(Buffer *buffer, PubCompPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
 
     return true;
 }
 
 bool decode_subscribe(Buffer *buffer, SubscribePayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
+
     payload->topic = utf8_string_decode(buffer);
     payload->qos = buffer->data[buffer->position++] & 0x03;
 
@@ -254,21 +275,32 @@ bool decode_subscribe(Buffer *buffer, SubscribePayload *payload) {
 }
 
 bool decode_suback(Buffer *buffer, SubAckPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
+
     payload->status = buffer->data[buffer->position++];
 
     return true;
 }
 
 bool decode_unsubscribe(Buffer *buffer, UnsubscribePayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
+
     payload->topic = utf8_string_decode(buffer);
 
     return true;
 }
 
 bool decode_unsuback(Buffer *buffer, UnsubAckPayload *payload) {
-    payload->packet_id = (buffer->data[buffer->position++] << 8) + buffer->data[buffer->position++];
+    payload->packet_id = 
+        (buffer->data[buffer->position] << 8) 
+        + buffer->data[buffer->position + 1];
+    buffer->position += 2;
 
     return true;
 }
@@ -614,4 +646,6 @@ Buffer *mqtt_packet_encode(MQTTPacket *packet) {
         case PacketTypeDisconnect:
             return encode_disconnect();
     }
+
+    return NULL;
 }
diff --git a/tests/Makefile b/tests/Makefile
index 5e3995d..fe3fe13 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -1,19 +1,22 @@
-SRCS=encode_packet.c decode_packet.c
+SRCS=encode_packet.c decode_packet.c connect_publish.c connect_subscribe.c
 OBJS=$(SRCS:%.c=%.o)
+COVERAGE_FILES=$(SRCS:%.c=%.gcno) $(SRCS:%.c=%.gcda)
 TARGETS=$(SRCS:%.c=%.test)
 
-CC=clang
-CFLAGS=-g -Os -Wall -I.. -I../src -I../platform -DDEBUG=1
+COVERAGE_FLAGS=-fprofile-arcs -ftest-coverage
+
+CC=gcc
+CFLAGS=-g -Os -Wall -I.. -I../src -I../platform -DDEBUG=1 $(COVERAGE_FLAGS)
 # -DTIMETRIAL
 LDFLAGS=
-LIBS=-L.. -lmqtt
+LIBS=-L.. -lmqtt-debug -lpthread
 
 all: $(TARGETS)
 
 %.test: %.o cputime.o
-	$(CC) $(LDFLAGS) -o $@ cputime.o $< $(LIBS)
-	./$@
-	rm $@
+	$(CC) $(COVERAGE_FLAGS) $(LDFLAGS) -o $@ cputime.o $< $(LIBS)
+	# ./$@
+	# rm $@
 
 %.o: %.c test.h
 	$(CC) $(CFLAGS) -o $@ -c $<
@@ -26,4 +29,5 @@ all: $(TARGETS)
 clean:
 	rm -f $(TARGETS)
 	rm -f $(OBJS)
+	rm -f $(COVERAGE_FILES)
 	rm -f *.e
diff --git a/tests/decode_packet.c b/tests/decode_packet.c
index e5701a2..142a06b 100644
--- a/tests/decode_packet.c
+++ b/tests/decode_packet.c
@@ -101,6 +101,21 @@ TestResult test_decode_connect_simple(void) {
     TEST_OK();
 }
 
+TestResult test_decode_connect_invalid(void) {
+    char data[] = {
+        0x10, 0x10, // header
+        0x00, 0x04, 'M', 'Q', 'T', 'X', 0x04, 0x02, 0x00, 0x0a, // var header
+        0x00, 0x04, 't', 'e', 's', 't' // client id
+    };
+    Buffer *buffer = buffer_from_data_copy(data, sizeof(data));
+    MQTTPacket *packet = mqtt_packet_decode(buffer);
+
+    TESTASSERT(packet == NULL, "Packet should not be valid");
+    buffer_release(buffer);
+
+    TEST_OK();
+}
+
 TestResult test_decode_connect_will(void) {
     char data[] = {
         0x10, 0x2d, // header
@@ -458,13 +473,6 @@ TestResult test_decode_disconnect(void) {
     TEST_OK();
 }
 
-// not implemented placeholder
-
-TestResult not_implemented(void) {
-    TESTRESULT(TestStatusSkipped, "Not implemented");
-}
-
-
 TESTS(
     TEST("Variable length int decode for 0", test_vl_int_data_0),
     TEST("Variable length int decode for 127", test_vl_int_data_127),
@@ -475,6 +483,7 @@ TESTS(
     TEST("UTF-8 string decode empty string", test_utf8_string_empty),
     TEST("UTF-8 string decode \"hello\"", test_utf8_string_hello),
     TEST("Decode Connect simple", test_decode_connect_simple),
+    TEST("Decode Connect invalid", test_decode_connect_invalid),
     TEST("Decode Connect with will", test_decode_connect_will),
     TEST("Decode Connect with auth", test_decode_connect_auth),
     TEST("Decode ConnAck", test_decode_connack),
diff --git a/tests/encode_packet.c b/tests/encode_packet.c
index c1ed28d..fbae50d 100644
--- a/tests/encode_packet.c
+++ b/tests/encode_packet.c
@@ -276,7 +276,7 @@ TestResult test_encode_publish_no_msg(void) {
 
 TestResult test_encode_publish_with_msg(void) {
     char data[] = {
-        0x33, 0x15, // header, qos1, retain
+        0x3b, 0x15, // header, qos1, retain
         0x00, 0x0a, 't', 'e', 's', 't', '/', 't', 'o', 'p', 'i', 'c',
         0x00, 0x0a, // packet id
         'p', 'a', 'y', 'l', 'o', 'a', 'd'
@@ -285,6 +285,7 @@ TestResult test_encode_publish_with_msg(void) {
 
     payload->qos = MQTT_QOS_1;
     payload->retain = true;
+    payload->duplicate = true;
     payload->topic = "test/topic";
     payload->packet_id = 10;
     payload->message = "payload";
@@ -489,13 +490,6 @@ TestResult test_encode_disconnect(void) {
     );
 }
 
-// not implemented placeholder
-
-TestResult not_implemented(void) {
-    TESTRESULT(TestStatusSkipped, "Not implemented");
-}
-
-
 TESTS(
     TEST("Variable length int size for 0", test_vl_int_0),
     TEST("Variable length int size for 127", test_vl_int_127),
diff --git a/tests/test.h b/tests/test.h
index 4e42d51..034fc20 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -64,6 +64,13 @@ static inline TestResult TESTMEMCMP(Buffer *template, Buffer *check) {
     }
 }
 
+// not implemented placeholder
+
+static TestResult not_implemented(void) {
+    TESTRESULT(TestStatusSkipped, "Not implemented");
+}
+
+
 void timetrial(DefinedTest *test);
 
 int main(int argc, char **argv) {