From dff0168859e73147f1ffac5f25b9f742ccbcded2 Mon Sep 17 00:00:00 2001
From: Brendan Bartels <bbartels@iastate.edu>
Date: Mon, 13 Mar 2017 07:59:29 -0500
Subject: [PATCH] wip: Add unit tests for communication.c packet receiving

---
 quad/library.mk                               |   2 +-
 quad/src/quad_app/.gitignore                  |   3 +-
 quad/src/quad_app/Makefile                    |   2 +-
 quad/src/quad_app/communication.c             |  98 +++---
 quad/src/quad_app/communication.h             |   2 +
 quad/src/quad_app/hw_iface.h                  |   4 +-
 quad/src/quad_app/initialize_components.c     |   3 -
 quad/src/quad_app/test/test_quad_app.c        | 294 ++++++++++++++++++
 quad/src/test/test.c                          |   2 +-
 quad/src/test/test.h                          |   6 +-
 .../modular_quad_pid/src/hw_impl_zybo.h       |   4 +-
 .../modular_quad_pid/src/hw_impl_zybo_uart.c  |  16 +-
 12 files changed, 370 insertions(+), 66 deletions(-)
 create mode 100644 quad/src/quad_app/test/test_quad_app.c

diff --git a/quad/library.mk b/quad/library.mk
index dc8a34d36..1515d75b9 100644
--- a/quad/library.mk
+++ b/quad/library.mk
@@ -52,4 +52,4 @@ $(LIBDIR):
 	mkdir $(LIBDIR)
 
 $(TESTBIN): $(TESTOBJECTS) $(OBJECTS) | default
-	$(GCC) -o $(TESTBIN) $^ -I$(INCDIR) -L$(LIBDIR) $(REQLIBS)
+	$(GCC) -g -o $(TESTBIN) $^ -I$(INCDIR) -L$(LIBDIR) $(REQLIBS)
diff --git a/quad/src/quad_app/.gitignore b/quad/src/quad_app/.gitignore
index 8d9968320..021c5c0ae 100644
--- a/quad/src/quad_app/.gitignore
+++ b/quad/src/quad_app/.gitignore
@@ -1,2 +1,3 @@
 obj-zybo/
-obj/
\ No newline at end of file
+obj/
+run_tests
\ No newline at end of file
diff --git a/quad/src/quad_app/Makefile b/quad/src/quad_app/Makefile
index 81aebd95c..aea297458 100644
--- a/quad/src/quad_app/Makefile
+++ b/quad/src/quad_app/Makefile
@@ -1,6 +1,6 @@
 TOP=../..
 
 NAME = quad_app
-REQLIBS = -ltest -lcomputation_graph
+REQLIBS = -ltest -lcomputation_graph -lm -lqueue
 
 include $(TOP)/library.mk
diff --git a/quad/src/quad_app/communication.c b/quad/src/quad_app/communication.c
index 3ca5cfeab..619257cb0 100644
--- a/quad/src/quad_app/communication.c
+++ b/quad/src/quad_app/communication.c
@@ -10,7 +10,6 @@
 // plus the maximum packet size that might be carried over from the last call,
 // plus 128 for a little buffer
 // (bit/s) * (seconds) / (10 bits / byte for UART)
-#define MAX_PACKET_SIZE 256
 #define PACKET_HEADER_SIZE 7
 #define UART_BUF_SIZE (((BAUD_RATE * (DESIRED_USEC_PER_LOOP / 1000) / 1000) / 10) + MAX_PACKET_SIZE + 128)
 
@@ -19,35 +18,62 @@
 // Declaration of interrupt handler
 void Handler(void *CallBackRef, u32 Event, unsigned int EventData);
 
-static u8 packet[MAX_PACKET_SIZE];
-static int bytes_recv = 0;
+u8 packet[MAX_PACKET_SIZE];
+int bytes_recv = 0;
 
 int try_receive_packet(struct UARTDriver *uart) {
 
-  // Find start of packet
   int attempts = 0;
-  while (bytes_recv == 0) {
-    if (attempts > MAX_PACKET_SIZE) return -1; // keep trying later
-    if (uart->read(uart, &packet[bytes_recv], 1)) return -1; // ran out
-    if (packet[bytes_recv] == 0xBE) bytes_recv += 1;
-    attempts += 1;
+  while (attempts++ < MAX_PACKET_SIZE) {
+    // Find start of packet
+    if (bytes_recv == 0) {
+      if (uart->read(uart, &packet[bytes_recv])) return -1; // uart empty
+      if (packet[bytes_recv] != 0xBE) {
+	continue; // keep looking
+      }
+      bytes_recv = 1;
+    }
+
+    // receive length bytes
+    while (bytes_recv < PACKET_HEADER_SIZE) {
+      if (uart->read(uart, &packet[bytes_recv])) return -1; // uart empty
+      bytes_recv += 1;
+    }
+
+    // Check if incoming packet is nonsensically large
+    unsigned short length = (packet[6] << 8 | packet[5]) + PACKET_HEADER_SIZE;
+    if (length >= MAX_PACKET_SIZE) {
+      bytes_recv = 0;
+      continue; // abort packet and start over
+    }
+
+    // Receive rest of packet
+    while (bytes_recv < length) {
+      if (uart->read(uart, &packet[bytes_recv])) return -1; // uart empty
+      bytes_recv += 1;
+    }
+
+    // Receive checksum and compare
+    if (bytes_recv < length + 1) {
+      if (uart->read(uart, &packet[bytes_recv])) return -1; // uart empty
+      unsigned char calculated_checksum = 0;
+      int i;
+      for(i = 0; i < length; i++) {
+	calculated_checksum ^= packet[i];
+      }
+      if (calculated_checksum != packet[bytes_recv]) {
+	bytes_recv = 0;
+	continue; // abort packet and start over
+      }
+      bytes_recv += 1;
+    }
+
+    // Packet ready
+    return 0;
   }
 
-  // Find length bytes in packet
-  while (bytes_recv < PACKET_HEADER_SIZE) {
-    if (uart->read(uart, &packet[bytes_recv++], 1)) return -1; // ran out
-    bytes_recv += 1;
-  }
-
-  // Wait for rest of data plus checksum (+1)
-  int length = (packet[6] << 8 | packet[5]) + PACKET_HEADER_SIZE + 1;
-  while (bytes_recv < length) {
-    if (uart->read(uart, &packet[bytes_recv++], 1)) return -1; // ran out
-    bytes_recv += 1;
-  }
-
-  // Fully recieved a packet
-  return 0;
+  // Try again later
+  return -1;
 }
 
 int process_packet(modular_structs_t *structs) {
@@ -59,21 +85,10 @@ int process_packet(modular_structs_t *structs) {
   meta.data_len = packet[6] << 8 | packet[5];
   unsigned char packet_checksum = packet[7+meta.data_len];
 
-  // Compute checksum
-  int i;
-  unsigned char calculated_checksum = 0;
-  for(i = 0; i < meta.data_len + 7; i++){
-    calculated_checksum ^= packet[i];
-  }
-  // Discard if checksum didn't match
-  if(packet_checksum != calculated_checksum) {
-    return -1;
-  }
-
   // Call appropriate function for packet
-  (* (MessageTypes[meta.msg_type].functionPtr))(structs,&meta,packet[PACKET_HEADER_SIZE], meta.data_len);
+  (* (MessageTypes[meta.msg_type].functionPtr))(structs,&meta,&packet[PACKET_HEADER_SIZE], meta.data_len);
 
-  // Done processing packets, reset
+  // Done processing packets, reset state
   bytes_recv = 0;
 
   return 0;
@@ -82,7 +97,7 @@ int process_packet(modular_structs_t *structs) {
 void process_received(modular_structs_t *structs) {
   // Parse as many packets as possible
   struct UARTDriver *uart = &structs->hardware_struct.uart;
-  while (try_receive_packet(uart)) {
+  while (!try_receive_packet(uart)) {
     process_packet(structs);
   }
 }
@@ -115,15 +130,14 @@ int send_data(struct UARTDriver *uart, u16 type_id, u16 msg_id, char* data, size
 	int i;
 	for(i = 0; i < PACKET_HEADER_SIZE; i++) {
 		packet_checksum ^= formattedHeader[i];
+		uart->write(uart, formattedHeader[i]);
 	}
 	for (i = 0; i < size; i++) {
 		packet_checksum ^= data[i];
+		uart->write(uart, (unsigned char) data[i]);
 	}
 
-	// Send header, data, and checksum
-	uart->write(uart, formattedHeader, PACKET_HEADER_SIZE);
-	uart->write(uart, (unsigned char *) data, size);
-	uart->write(uart, &packet_checksum, 1);
+	uart->write(uart, packet_checksum);
 
 	return 0;
 }
diff --git a/quad/src/quad_app/communication.h b/quad/src/quad_app/communication.h
index 4c8b86c80..8655aad00 100644
--- a/quad/src/quad_app/communication.h
+++ b/quad/src/quad_app/communication.h
@@ -6,6 +6,8 @@
 #include "commands.h"
 #include "hw_iface.h"
 
+#define MAX_PACKET_SIZE 256
+
 int initUartComms();
 void process_received(modular_structs_t *structs);
 int send_data(struct UARTDriver *uart, u16 type_id, u16 msg_id, char* data, size_t size);
diff --git a/quad/src/quad_app/hw_iface.h b/quad/src/quad_app/hw_iface.h
index 79898e582..6e6bbaa0f 100644
--- a/quad/src/quad_app/hw_iface.h
+++ b/quad/src/quad_app/hw_iface.h
@@ -29,8 +29,8 @@ struct PWMInputDriver {
 struct UARTDriver {
   void *state;
   int (*reset)(struct UARTDriver *self);
-  int (*write)(struct UARTDriver *self, unsigned char *data, unsigned int length);
-  int (*read)(struct UARTDriver *self, unsigned char *buff, unsigned int length);
+  int (*write)(struct UARTDriver *self, unsigned char c);
+  int (*read)(struct UARTDriver *self, unsigned char *c);
 };
 
 struct TimerDriver {
diff --git a/quad/src/quad_app/initialize_components.c b/quad/src/quad_app/initialize_components.c
index e5f73528f..3ef345150 100644
--- a/quad/src/quad_app/initialize_components.c
+++ b/quad/src/quad_app/initialize_components.c
@@ -50,9 +50,6 @@ int init_structs(modular_structs_t *structs) {
   // Initialize the controller
   control_algorithm_init(&structs->parameter_struct);
 
-  // Xilinx given initialization
-  init_platform();
-
   // Initialize loop timers
   struct TimerDriver *global_timer = &structs->hardware_struct.global_timer;
   struct TimerDriver *axi_timer = &structs->hardware_struct.axi_timer;
diff --git a/quad/src/quad_app/test/test_quad_app.c b/quad/src/quad_app/test/test_quad_app.c
new file mode 100644
index 000000000..8c9a45dea
--- /dev/null
+++ b/quad/src/quad_app/test/test_quad_app.c
@@ -0,0 +1,294 @@
+#include "communication.h"
+#include <string.h>
+#include "queue.h"
+#include "test.h"
+
+struct Queue *queue;
+struct UARTDriver *uart;
+
+int mock_uart_read(struct UARTDriver *self, unsigned char *c) {
+  return queue_remove(queue, c);
+}
+
+struct UARTDriver * mock_uart_malloc() {
+  struct UARTDriver *uart = malloc(sizeof(struct UARTDriver));
+  uart->read = mock_uart_read;
+  return uart;
+}
+
+void queue_add_corruption(struct Queue *q, int num) {
+  int val = 0xAA;
+  int i;
+  for (i = 0; i < num; i += 1) {
+    val = (val << 1) ^ i;
+    if (val == 0xBE) queue_add(q, 0);
+    else queue_add(q, val);
+  }
+}
+
+void queue_add_short(struct Queue *q, short val) {
+  queue_add(q, val & 0x00FF);
+  queue_add(q, val >> 8);
+}
+
+// Return checksum
+unsigned char queue_add_packet(struct Queue *q,
+		unsigned short type,
+		unsigned short id,
+		unsigned short length,
+		unsigned char *data) {
+  queue_add(q, 0xBE);
+  queue_add(q, type & 0x00FF);
+  queue_add(q, type >> 8);
+  queue_add(q, id & 0x00FF);
+  queue_add(q, id >> 8);
+  queue_add(q, length & 0x00FF);
+  queue_add(q, length >> 8);
+
+  unsigned char checksum = 0;
+  checksum ^= 0xBE;
+  checksum ^= type & 0x00FF;
+  checksum ^= type >> 8;
+  checksum ^= id & 0x00FF;
+  checksum ^= id >> 8;
+  checksum ^= length & 0x00FF;
+  checksum ^= length >> 8;
+  int i;
+  for (i = 0; i < length; i += 1) {
+    queue_add(q, data[i]);
+    checksum ^= data[i];
+  }
+  queue_add(q, checksum);
+  return checksum;
+}
+
+// Spying into communication.c's global variables
+extern u8 packet[MAX_PACKET_SIZE];
+extern int bytes_recv;
+extern unsigned char packet_checksum;
+
+// Test fails when no BE and run out
+int test_try_receive_packet_fails_when_no_BE_and_run_out() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(5);
+  queue_add(queue, 0);
+  queue_add(queue, 0);
+  queue_add(queue, 1);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 0);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 0);
+
+  return 0;
+}
+
+// Test fails when no BE and too much (check resume)
+int test_try_receive_packet_fails_when_no_BE_and_too_much() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  int size = 255;
+  queue = queue_malloc(size);
+  queue_add_corruption(queue, size);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 0);
+
+  // Ensure that we quit trying
+  test_assert(size - queue_size(queue) <= MAX_PACKET_SIZE);
+
+  return 0;
+}
+
+// Test fails when BE and run out (check resume)
+int test_try_receive_packet_fails_when_BE_and_run_out() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(100);
+  queue_add_corruption(queue, 10);
+  queue_add(queue, 0xBE);
+
+  queue_add(queue, 1);
+  queue_add(queue, 2);
+  queue_add(queue, 3);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 4);
+
+  test_assert(packet[0] == 0xBE);
+  test_assert(packet[1] == 1);
+  test_assert(packet[2] == 2);
+  test_assert(packet[3] == 3);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 4);
+
+  return 0;
+}
+
+// Test fails when BE and nonsensical length (check resume)
+int test_try_receive_packet_fails_when_BE_and_big_length() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(500);
+  int i;
+  queue_add_corruption(queue, 10);
+  queue_add(queue, 0xBE);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 500);
+  for (i = 0; i < 10; i += 1) queue_add_corruption(queue, i);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 0);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 0);
+
+  return 0;
+}
+
+// Test fails when BE and length and run out (check resume)
+int test_try_receive_packet_fails_when_BE_length_and_run_out() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(500);
+  int i;
+  queue_add_corruption(queue, 20);
+  queue_add(queue, 0xBE);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 4);
+  unsigned char data[4] = {1, 2, 3, 4};
+  for (i = 0; i < 2; i += 1) queue_add(queue, data[i]);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 9);
+  test_assert(packet[0] == 0xBE);
+  test_assert(packet[1] == 0);
+  test_assert(packet[2] == 0);
+  test_assert(packet[3] == 0);
+  test_assert(packet[4] == 0);
+  test_assert(packet[5] == 4);
+  test_assert(packet[6] == 0);
+  test_assert(packet[7] == 1);
+  test_assert(packet[8] == 2);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 9);
+
+  return 0;
+}
+
+// Test fails when BE and length and data and run out (check resume)
+int test_try_receive_packet_fails_when_BE_length_data_and_run_out() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(500);
+  int i;
+  queue_add_corruption(queue, 10);
+  queue_add(queue, 0xBE);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 4);
+  unsigned char data[4] = {1, 2, 3, 4};
+  for (i = 0; i < 4; i += 1) queue_add(queue, data[i]);
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 11);
+  test_assert(packet[0] == 0xBE);
+  test_assert(packet[1] == 0);
+  test_assert(packet[2] == 0);
+  test_assert(packet[3] == 0);
+  test_assert(packet[4] == 0);
+  test_assert(packet[5] == 4);
+  test_assert(packet[6] == 0);
+  test_assert(packet[7] == 1);
+  test_assert(packet[8] == 2);
+  test_assert(packet[9] == 3);
+  test_assert(packet[10] == 4);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 11);
+
+  return 0;
+}
+
+// Test fails when BE, length, data, and checksum fails
+int test_try_receive_packet_fails_when_BE_length_data_and_bad_checksum() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(500);
+  int i;
+  queue_add_corruption(queue, 10);
+  queue_add(queue, 0xBE);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 0);
+  queue_add_short(queue, 4);
+  unsigned char data[4] = {1, 2, 3, 4};
+  for (i = 0; i < 4; i += 1) queue_add(queue, data[i]);
+  queue_add(queue, 0xFF); // bad checksum
+  queue_add(queue, 0xBE); // next start
+
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 1);
+  test_assert(packet[0] == 0xBE);
+
+  // Try again to verify that we actually ran out
+  test_assert(try_receive_packet(uart));
+  test_assert(bytes_recv == 1);
+
+  return 0;
+}
+
+// Test succeeds when BE, length, data, checksum
+int test_try_receive_packet_succeeds() {
+  bytes_recv = 0;
+  uart = mock_uart_malloc();
+  queue = queue_malloc(500);
+  int i;
+  queue_add_corruption(queue, 10);;
+  unsigned char data[4] = {1, 2, 3, 4};
+  unsigned char checksum = queue_add_packet(queue, 0, 0, 4, data);
+
+  int failure = try_receive_packet(uart);
+  test_assert(!failure);
+  test_assert(bytes_recv == 12);
+  test_assert(packet[0] == 0xBE);
+  test_assert(packet[1] == 0);
+  test_assert(packet[2] == 0);
+  test_assert(packet[3] == 0);
+  test_assert(packet[4] == 0);
+  test_assert(packet[5] == 4);
+  test_assert(packet[6] == 0);
+  test_assert(packet[7] == 1);
+  test_assert(packet[8] == 2);
+  test_assert(packet[9] == 3);
+  test_assert(packet[10] == 4);
+  test_assert(packet[11] == checksum);
+
+  // Try again to verify that we don't lose the ready packet
+  test_assert(!try_receive_packet(uart));
+  test_assert(bytes_recv == 12);
+
+  return 0;
+}
+
+int main() {
+  TEST(test_try_receive_packet_fails_when_no_BE_and_run_out);
+  TEST(test_try_receive_packet_fails_when_no_BE_and_too_much);
+  TEST(test_try_receive_packet_fails_when_BE_and_run_out);
+  TEST(test_try_receive_packet_fails_when_BE_and_big_length);
+  TEST(test_try_receive_packet_fails_when_BE_length_and_run_out);
+  TEST(test_try_receive_packet_fails_when_BE_length_data_and_run_out);
+  TEST(test_try_receive_packet_fails_when_BE_length_data_and_bad_checksum);
+  TEST(test_try_receive_packet_succeeds);
+  return test_summary();
+}
diff --git a/quad/src/test/test.c b/quad/src/test/test.c
index c571c4a91..98863ecd8 100644
--- a/quad/src/test/test.c
+++ b/quad/src/test/test.c
@@ -1,7 +1,7 @@
 #include "test.h"
 
 static int num_tests = 0;
-static struct Test tests[128];
+static struct Test tests[255];
 static int longest_test_name = 0;
 static int num_assertions = 0;
 
diff --git a/quad/src/test/test.h b/quad/src/test/test.h
index 4fe3ab12e..ac50248ec 100644
--- a/quad/src/test/test.h
+++ b/quad/src/test/test.h
@@ -7,9 +7,11 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#define TEST(name) test(name, #name)
+
 struct Test {
-  char name[64];
-  char result_msg[32];
+  char name[255];
+  char result_msg[255];
   unsigned char failed;
 };
 
diff --git a/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo.h b/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo.h
index aef58b58e..7be77d634 100644
--- a/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo.h
+++ b/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo.h
@@ -19,8 +19,8 @@
 #include "sleep.h"
 
 int zybo_uart_reset(struct UARTDriver *self);
-int zybo_uart_write(struct UARTDriver *self, unsigned char *data, unsigned int length);
-int zybo_uart_read(struct UARTDriver *self, unsigned char *buff, unsigned int length);
+int zybo_uart_write(struct UARTDriver *self, unsigned char c);
+int zybo_uart_read(struct UARTDriver *self, unsigned char *c);
 
 int zybo_pwm_output_reset(struct PWMOutputDriver *self);
 int zybo_pwm_output_write(struct PWMOutputDriver *self, unsigned int channel, unsigned long pulse_width_us);
diff --git a/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo_uart.c b/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo_uart.c
index 9d87f6444..549c21f8f 100644
--- a/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo_uart.c
+++ b/quad/xsdk_workspace/modular_quad_pid/src/hw_impl_zybo_uart.c
@@ -68,20 +68,14 @@ int zybo_uart_reset(struct UARTDriver *self) {
   return 0;
 }
 
-int zybo_uart_write(struct UARTDriver *self, unsigned char *data, unsigned int length) {
-  XUartPs *inst = self->state;
-  int i;
-  for (i = 0; i < length; i += 1) {
-    XUartPs_SendByte(inst->Config.BaseAddress, data[i]);
-  }
-
+int zybo_uart_write(struct UARTDriver *self, unsigned char c) {
+  XUartPs_SendByte(inst->Config.BaseAddress, data[i]);
   return 0;
 }
 
-int zybo_uart_read(struct UARTDriver *self, unsigned char *buff, unsigned int length) {
-  int i = 0;
-  while (queue_remove(&queue, &buff[i]) == 0 && i < length) i += 1;
-  return i;
+int zybo_uart_read(struct UARTDriver *self, unsigned char *c) {
+  if (!queue_remove(&queue, c)) return -1;
+  else return 0;
 }
 
 //This is copied from xuart driver
-- 
GitLab