summaryrefslogtreecommitdiff
path: root/quantum/serial_link/tests
diff options
context:
space:
mode:
authorFred Sundvik <fsundvik@gmail.com>2016-07-06 13:26:20 +0300
committerFred Sundvik <fsundvik@gmail.com>2016-07-06 13:26:20 +0300
commitd5e7603d551a31836bf0c59db259ddc3593a1aa7 (patch)
treecf2ae7163eb7431e24e8a93752ef4fd18d292cd0 /quantum/serial_link/tests
parentc5bf090d16cf1fc4d8186f1e8fbbd62457e72da6 (diff)
Remove extra serial_link subdirectory
Diffstat (limited to 'quantum/serial_link/tests')
-rw-r--r--quantum/serial_link/tests/Makefile61
-rw-r--r--quantum/serial_link/tests/byte_stuffer_tests.c506
-rw-r--r--quantum/serial_link/tests/frame_router_tests.c231
-rw-r--r--quantum/serial_link/tests/frame_validator_tests.c101
-rw-r--r--quantum/serial_link/tests/transport_tests.c168
-rw-r--r--quantum/serial_link/tests/triple_buffered_object_tests.c82
6 files changed, 1149 insertions, 0 deletions
diff --git a/quantum/serial_link/tests/Makefile b/quantum/serial_link/tests/Makefile
new file mode 100644
index 0000000000..1b072c6f1d
--- /dev/null
+++ b/quantum/serial_link/tests/Makefile
@@ -0,0 +1,61 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2016 Fred Sundvik
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+CC = gcc
+CFLAGS =
+INCLUDES = -I. -I../../
+LDFLAGS = -L$(BUILDDIR)/cgreen/build-c/src -shared
+LDLIBS = -lcgreen
+UNITOBJ = $(BUILDDIR)/serialtest/unitobj
+DEPDIR = $(BUILDDIR)/serialtest/unit.d
+UNITTESTS = $(BUILDDIR)/serialtest/unittests
+DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPDIR)/$*.Td
+EXT = .so
+UNAME := $(shell uname)
+ifneq (, $(findstring MINGW, $(UNAME)))
+ EXT = .dll
+endif
+ifneq (, $(findstring CYGWIN, $(UNAME)))
+ EXT = .dll
+endif
+
+SRC = $(wildcard *.c)
+TESTFILES = $(patsubst %.c, $(UNITTESTS)/%$(EXT), $(SRC))
+$(shell mkdir -p $(DEPDIR) >/dev/null)
+
+test: $(TESTFILES)
+ @$(BUILDDIR)/cgreen/build-c/tools/cgreen-runner --color $(TESTFILES)
+
+$(UNITTESTS)/%$(EXT): $(UNITOBJ)/%.o
+ @mkdir -p $(UNITTESTS)
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(UNITOBJ)/%.o : %.c
+$(UNITOBJ)/%.o: %.c $(DEPDIR)/%.d
+ @mkdir -p $(UNITOBJ)
+ $(CC) $(CFLAGS) $(DEPFLAGS) $(INCLUDES) -c $< -o $@
+ @mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
+
+$(DEPDIR)/%.d: ;
+.PRECIOUS: $(DEPDIR)/%.d
+
+-include $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRC))) \ No newline at end of file
diff --git a/quantum/serial_link/tests/byte_stuffer_tests.c b/quantum/serial_link/tests/byte_stuffer_tests.c
new file mode 100644
index 0000000000..64b170e8c1
--- /dev/null
+++ b/quantum/serial_link/tests/byte_stuffer_tests.c
@@ -0,0 +1,506 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Fred Sundvik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include "serial_link/protocol/byte_stuffer.h"
+#include "serial_link/protocol/byte_stuffer.c"
+#include "serial_link/protocol/frame_validator.h"
+#include "serial_link/protocol/physical.h"
+
+static uint8_t sent_data[MAX_FRAME_SIZE*2];
+static uint16_t sent_data_size;
+
+Describe(ByteStuffer);
+BeforeEach(ByteStuffer) {
+ init_byte_stuffer();
+ sent_data_size = 0;
+}
+AfterEach(ByteStuffer) {}
+
+void validator_recv_frame(uint8_t link, uint8_t* data, uint16_t size) {
+ mock(data, size);
+}
+
+void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
+ memcpy(sent_data + sent_data_size, data, size);
+ sent_data_size += size;
+}
+
+Ensure(ByteStuffer, receives_no_frame_for_a_single_zero_byte) {
+ never_expect(validator_recv_frame);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_no_frame_for_a_single_FF_byte) {
+ never_expect(validator_recv_frame);
+ byte_stuffer_recv_byte(0, 0xFF);
+}
+
+Ensure(ByteStuffer, receives_no_frame_for_a_single_random_byte) {
+ never_expect(validator_recv_frame);
+ byte_stuffer_recv_byte(0, 0x4A);
+}
+
+Ensure(ByteStuffer, receives_no_frame_for_a_zero_length_frame) {
+ never_expect(validator_recv_frame);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_single_byte_valid_frame) {
+ uint8_t expected[] = {0x37};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(1)),
+ when(data, is_equal_to_contents_of(expected, 1))
+ );
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 0x37);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_three_bytes_valid_frame) {
+ uint8_t expected[] = {0x37, 0x99, 0xFF};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(3)),
+ when(data, is_equal_to_contents_of(expected, 3))
+ );
+ byte_stuffer_recv_byte(0, 4);
+ byte_stuffer_recv_byte(0, 0x37);
+ byte_stuffer_recv_byte(0, 0x99);
+ byte_stuffer_recv_byte(0, 0xFF);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_single_zero_valid_frame) {
+ uint8_t expected[] = {0};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(1)),
+ when(data, is_equal_to_contents_of(expected, 1))
+ );
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_valid_frame_with_zeroes) {
+ uint8_t expected[] = {5, 0, 3, 0};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(expected, 4))
+ );
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 5);
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 3);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_two_valid_frames) {
+ uint8_t expected1[] = {5, 0};
+ uint8_t expected2[] = {3};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(2)),
+ when(data, is_equal_to_contents_of(expected1, 2))
+ );
+ expect(validator_recv_frame,
+ when(size, is_equal_to(1)),
+ when(data, is_equal_to_contents_of(expected2, 1))
+ );
+ byte_stuffer_recv_byte(1, 2);
+ byte_stuffer_recv_byte(1, 5);
+ byte_stuffer_recv_byte(1, 1);
+ byte_stuffer_recv_byte(1, 0);
+ byte_stuffer_recv_byte(1, 2);
+ byte_stuffer_recv_byte(1, 3);
+ byte_stuffer_recv_byte(1, 0);
+}
+
+Ensure(ByteStuffer, receives_valid_frame_after_unexpected_zero) {
+ uint8_t expected[] = {5, 7};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(2)),
+ when(data, is_equal_to_contents_of(expected, 2))
+ );
+ byte_stuffer_recv_byte(1, 3);
+ byte_stuffer_recv_byte(1, 1);
+ byte_stuffer_recv_byte(1, 0);
+ byte_stuffer_recv_byte(1, 3);
+ byte_stuffer_recv_byte(1, 5);
+ byte_stuffer_recv_byte(1, 7);
+ byte_stuffer_recv_byte(1, 0);
+}
+
+Ensure(ByteStuffer, receives_valid_frame_after_unexpected_non_zero) {
+ uint8_t expected[] = {5, 7};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(2)),
+ when(data, is_equal_to_contents_of(expected, 2))
+ );
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 9);
+ byte_stuffer_recv_byte(0, 4); // This should have been zero
+ byte_stuffer_recv_byte(0, 0);
+ byte_stuffer_recv_byte(0, 3);
+ byte_stuffer_recv_byte(0, 5);
+ byte_stuffer_recv_byte(0, 7);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_and_then_end_of_frame) {
+ uint8_t expected[254];
+ int i;
+ for (i=0;i<254;i++) {
+ expected[i] = i + 1;
+ }
+ expect(validator_recv_frame,
+ when(size, is_equal_to(254)),
+ when(data, is_equal_to_contents_of(expected, 254))
+ );
+ byte_stuffer_recv_byte(0, 0xFF);
+ for (i=0;i<254;i++) {
+ byte_stuffer_recv_byte(0, i+1);
+ }
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_non_zero) {
+ uint8_t expected[255];
+ int i;
+ for (i=0;i<254;i++) {
+ expected[i] = i + 1;
+ }
+ expected[254] = 7;
+ expect(validator_recv_frame,
+ when(size, is_equal_to(255)),
+ when(data, is_equal_to_contents_of(expected, 255))
+ );
+ byte_stuffer_recv_byte(0, 0xFF);
+ for (i=0;i<254;i++) {
+ byte_stuffer_recv_byte(0, i+1);
+ }
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 7);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_a_valid_frame_with_over254_non_zeroes_next_byte_is_zero) {
+ uint8_t expected[255];
+ int i;
+ for (i=0;i<254;i++) {
+ expected[i] = i + 1;
+ }
+ expected[254] = 0;
+ expect(validator_recv_frame,
+ when(size, is_equal_to(255)),
+ when(data, is_equal_to_contents_of(expected, 255))
+ );
+ byte_stuffer_recv_byte(0, 0xFF);
+ for (i=0;i<254;i++) {
+ byte_stuffer_recv_byte(0, i+1);
+ }
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_two_long_frames_and_some_more) {
+ uint8_t expected[515];
+ int i;
+ int j;
+ for (j=0;j<2;j++) {
+ for (i=0;i<254;i++) {
+ expected[i+254*j] = i + 1;
+ }
+ }
+ for (i=0;i<7;i++) {
+ expected[254*2+i] = i + 1;
+ }
+ expect(validator_recv_frame,
+ when(size, is_equal_to(515)),
+ when(data, is_equal_to_contents_of(expected, 510))
+ );
+ byte_stuffer_recv_byte(0, 0xFF);
+ for (i=0;i<254;i++) {
+ byte_stuffer_recv_byte(0, i+1);
+ }
+ byte_stuffer_recv_byte(0, 0xFF);
+ for (i=0;i<254;i++) {
+ byte_stuffer_recv_byte(0, i+1);
+ }
+ byte_stuffer_recv_byte(0, 8);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 3);
+ byte_stuffer_recv_byte(0, 4);
+ byte_stuffer_recv_byte(0, 5);
+ byte_stuffer_recv_byte(0, 6);
+ byte_stuffer_recv_byte(0, 7);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, receives_an_all_zeros_frame_that_is_maximum_size) {
+ uint8_t expected[MAX_FRAME_SIZE] = {};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(MAX_FRAME_SIZE)),
+ when(data, is_equal_to_contents_of(expected, MAX_FRAME_SIZE))
+ );
+ int i;
+ byte_stuffer_recv_byte(0, 1);
+ for(i=0;i<MAX_FRAME_SIZE;i++) {
+ byte_stuffer_recv_byte(0, 1);
+ }
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, doesnt_recv_a_frame_thats_too_long_all_zeroes) {
+ uint8_t expected[1] = {0};
+ never_expect(validator_recv_frame);
+ int i;
+ byte_stuffer_recv_byte(0, 1);
+ for(i=0;i<MAX_FRAME_SIZE;i++) {
+ byte_stuffer_recv_byte(0, 1);
+ }
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, received_frame_is_aborted_when_its_too_long) {
+ uint8_t expected[1] = {1};
+ expect(validator_recv_frame,
+ when(size, is_equal_to(1)),
+ when(data, is_equal_to_contents_of(expected, 1))
+ );
+ int i;
+ byte_stuffer_recv_byte(0, 1);
+ for(i=0;i<MAX_FRAME_SIZE;i++) {
+ byte_stuffer_recv_byte(0, 1);
+ }
+ byte_stuffer_recv_byte(0, 2);
+ byte_stuffer_recv_byte(0, 1);
+ byte_stuffer_recv_byte(0, 0);
+}
+
+Ensure(ByteStuffer, does_nothing_when_sending_zero_size_frame) {
+ assert_that(sent_data_size, is_equal_to(0));
+ byte_stuffer_send_frame(0, NULL, 0);
+}
+
+Ensure(ByteStuffer, send_one_byte_frame) {
+ uint8_t data[] = {5};
+ byte_stuffer_send_frame(1, data, 1);
+ uint8_t expected[] = {2, 5, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_two_byte_frame) {
+ uint8_t data[] = {5, 0x77};
+ byte_stuffer_send_frame(0, data, 2);
+ uint8_t expected[] = {3, 5, 0x77, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_one_byte_frame_with_zero) {
+ uint8_t data[] = {0};
+ byte_stuffer_send_frame(0, data, 1);
+ uint8_t expected[] = {1, 1, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_two_byte_frame_starting_with_zero) {
+ uint8_t data[] = {0, 9};
+ byte_stuffer_send_frame(1, data, 2);
+ uint8_t expected[] = {1, 2, 9, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_two_byte_frame_starting_with_non_zero) {
+ uint8_t data[] = {9, 0};
+ byte_stuffer_send_frame(1, data, 2);
+ uint8_t expected[] = {2, 9, 1, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_three_byte_frame_zero_in_the_middle) {
+ uint8_t data[] = {9, 0, 0x68};
+ byte_stuffer_send_frame(0, data, 3);
+ uint8_t expected[] = {2, 9, 2, 0x68, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_three_byte_frame_data_in_the_middle) {
+ uint8_t data[] = {0, 0x55, 0};
+ byte_stuffer_send_frame(0, data, 3);
+ uint8_t expected[] = {1, 2, 0x55, 1, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_three_byte_frame_with_all_zeroes) {
+ uint8_t data[] = {0, 0, 0};
+ byte_stuffer_send_frame(0, data, 3);
+ uint8_t expected[] = {1, 1, 1, 1, 0};
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_frame_with_254_non_zeroes) {
+ uint8_t data[254];
+ int i;
+ for(i=0;i<254;i++) {
+ data[i] = i + 1;
+ }
+ byte_stuffer_send_frame(0, data, 254);
+ uint8_t expected[256];
+ expected[0] = 0xFF;
+ for(i=1;i<255;i++) {
+ expected[i] = i;
+ }
+ expected[255] = 0;
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_frame_with_255_non_zeroes) {
+ uint8_t data[255];
+ int i;
+ for(i=0;i<255;i++) {
+ data[i] = i + 1;
+ }
+ byte_stuffer_send_frame(0, data, 255);
+ uint8_t expected[258];
+ expected[0] = 0xFF;
+ for(i=1;i<255;i++) {
+ expected[i] = i;
+ }
+ expected[255] = 2;
+ expected[256] = 255;
+ expected[257] = 0;
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_frame_with_254_non_zeroes_followed_by_zero) {
+ uint8_t data[255];
+ int i;
+ for(i=0;i<254;i++) {
+ data[i] = i + 1;
+ }
+ data[255] = 0;
+ byte_stuffer_send_frame(0, data, 255);
+ uint8_t expected[258];
+ expected[0] = 0xFF;
+ for(i=1;i<255;i++) {
+ expected[i] = i;
+ }
+ expected[255] = 1;
+ expected[256] = 1;
+ expected[257] = 0;
+ assert_that(sent_data_size, is_equal_to(sizeof(expected)));
+ assert_that(sent_data, is_equal_to_contents_of(expected, sizeof(expected)));
+}
+
+Ensure(ByteStuffer, sends_and_receives_full_roundtrip_small_packet) {
+ uint8_t original_data[] = { 1, 2, 3};
+ byte_stuffer_send_frame(0, original_data, sizeof(original_data));
+ expect(validator_recv_frame,
+ when(size, is_equal_to(sizeof(original_data))),
+ when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
+ );
+ int i;
+ for(i=0;i<sent_data_size;i++) {
+ byte_stuffer_recv_byte(1, sent_data[i]);
+ }
+}
+
+Ensure(ByteStuffer, sends_and_receives_full_roundtrip_small_packet_with_zeros) {
+ uint8_t original_data[] = { 1, 0, 3, 0, 0, 9};
+ byte_stuffer_send_frame(1, original_data, sizeof(original_data));
+ expect(validator_recv_frame,
+ when(size, is_equal_to(sizeof(original_data))),
+ when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
+ );
+ int i;
+ for(i=0;i<sent_data_size;i++) {
+ byte_stuffer_recv_byte(0, sent_data[i]);
+ }
+}
+
+Ensure(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes) {
+ uint8_t original_data[254];
+ int i;
+ for(i=0;i<254;i++) {
+ original_data[i] = i + 1;
+ }
+ byte_stuffer_send_frame(0, original_data, sizeof(original_data));
+ expect(validator_recv_frame,
+ when(size, is_equal_to(sizeof(original_data))),
+ when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
+ );
+ for(i=0;i<sent_data_size;i++) {
+ byte_stuffer_recv_byte(1, sent_data[i]);
+ }
+}
+
+Ensure(ByteStuffer, sends_and_receives_full_roundtrip_256_bytes) {
+ uint8_t original_data[256];
+ int i;
+ for(i=0;i<254;i++) {
+ original_data[i] = i + 1;
+ }
+ original_data[254] = 22;
+ original_data[255] = 23;
+ byte_stuffer_send_frame(0, original_data, sizeof(original_data));
+ expect(validator_recv_frame,
+ when(size, is_equal_to(sizeof(original_data))),
+ when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
+ );
+ for(i=0;i<sent_data_size;i++) {
+ byte_stuffer_recv_byte(1, sent_data[i]);
+ }
+}
+
+Ensure(ByteStuffer, sends_and_receives_full_roundtrip_254_bytes_and_then_zero) {
+ uint8_t original_data[255];
+ int i;
+ for(i=0;i<254;i++) {
+ original_data[i] = i + 1;
+ }
+ original_data[254] = 0;
+ byte_stuffer_send_frame(0, original_data, sizeof(original_data));
+ expect(validator_recv_frame,
+ when(size, is_equal_to(sizeof(original_data))),
+ when(data, is_equal_to_contents_of(original_data, sizeof(original_data)))
+ );
+ for(i=0;i<sent_data_size;i++) {
+ byte_stuffer_recv_byte(1, sent_data[i]);
+ }
+}
diff --git a/quantum/serial_link/tests/frame_router_tests.c b/quantum/serial_link/tests/frame_router_tests.c
new file mode 100644
index 0000000000..6c806fa939
--- /dev/null
+++ b/quantum/serial_link/tests/frame_router_tests.c
@@ -0,0 +1,231 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Fred Sundvik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include "serial_link/protocol/byte_stuffer.c"
+#include "serial_link/protocol/frame_validator.c"
+#include "serial_link/protocol/frame_router.c"
+#include "serial_link/protocol/transport.h"
+
+static uint8_t received_data[256];
+static uint16_t received_data_size;
+
+typedef struct {
+ uint8_t sent_data[256];
+ uint16_t sent_data_size;
+} receive_buffer_t;
+
+typedef struct {
+ receive_buffer_t send_buffers[2];
+} router_buffer_t;
+
+router_buffer_t router_buffers[8];
+
+router_buffer_t* current_router_buffer;
+
+
+Describe(FrameRouter);
+BeforeEach(FrameRouter) {
+ init_byte_stuffer();
+ memset(router_buffers, 0, sizeof(router_buffers));
+ current_router_buffer = 0;
+}
+AfterEach(FrameRouter) {}
+
+typedef struct {
+ uint32_t data;
+ uint8_t extra[16];
+} frame_buffer_t;
+
+
+void send_data(uint8_t link, const uint8_t* data, uint16_t size) {
+ receive_buffer_t* buffer = &current_router_buffer->send_buffers[link];
+ memcpy(buffer->sent_data + buffer->sent_data_size, data, size);
+ buffer->sent_data_size += size;
+}
+
+static void receive_data(uint8_t link, uint8_t* data, uint16_t size) {
+ int i;
+ for(i=0;i<size;i++) {
+ byte_stuffer_recv_byte(link, data[i]);
+ }
+}
+
+static void activate_router(uint8_t num) {
+ current_router_buffer = router_buffers + num;
+ router_set_master(num==0);
+}
+
+static void simulate_transport(uint8_t from, uint8_t to) {
+ activate_router(to);
+ if (from > to) {
+ receive_data(DOWN_LINK,
+ router_buffers[from].send_buffers[UP_LINK].sent_data,
+ router_buffers[from].send_buffers[UP_LINK].sent_data_size);
+ }
+ else if(to > from) {
+ receive_data(UP_LINK,
+ router_buffers[from].send_buffers[DOWN_LINK].sent_data,
+ router_buffers[from].send_buffers[DOWN_LINK].sent_data_size);
+ }
+}
+
+void transport_recv_frame(uint8_t from, uint8_t* data, uint16_t size) {
+ mock(from, data, size);
+}
+
+
+Ensure(FrameRouter, master_broadcast_is_received_by_everyone) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(0);
+ router_send_frame(0xFF, (uint8_t*)&data, 4);
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(0)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(0, 1);
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(0)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(1, 2);
+ assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, master_send_is_received_by_targets) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(0);
+ router_send_frame((1 << 1) | (1 << 2), (uint8_t*)&data, 4);
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+
+ simulate_transport(0, 1);
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(0)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(1, 2);
+ assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(0)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(2, 3);
+ assert_that(router_buffers[3].send_buffers[DOWN_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[3].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, first_link_sends_to_master) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(1);
+ router_send_frame(0, (uint8_t*)&data, 4);
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(1)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(1, 0);
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, second_link_sends_to_master) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(2);
+ router_send_frame(0, (uint8_t*)&data, 4);
+ assert_that(router_buffers[2].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[2].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+
+ simulate_transport(2, 1);
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+
+ expect(transport_recv_frame,
+ when(from, is_equal_to(2)),
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(&data.data, 4))
+ );
+ simulate_transport(1, 0);
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, master_sends_to_master_does_nothing) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(0);
+ router_send_frame(0, (uint8_t*)&data, 4);
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, link_sends_to_other_link_does_nothing) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(1);
+ router_send_frame(2, (uint8_t*)&data, 4);
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+}
+
+Ensure(FrameRouter, master_receives_on_uplink_does_nothing) {
+ frame_buffer_t data;
+ data.data = 0xAB7055BB;
+ activate_router(1);
+ router_send_frame(0, (uint8_t*)&data, 4);
+ assert_that(router_buffers[1].send_buffers[UP_LINK].sent_data_size, is_greater_than(0));
+ assert_that(router_buffers[1].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+
+ never_expect(transport_recv_frame);
+ activate_router(0);
+ receive_data(UP_LINK,
+ router_buffers[1].send_buffers[UP_LINK].sent_data,
+ router_buffers[1].send_buffers[UP_LINK].sent_data_size);
+ assert_that(router_buffers[0].send_buffers[UP_LINK].sent_data_size, is_equal_to(0));
+ assert_that(router_buffers[0].send_buffers[DOWN_LINK].sent_data_size, is_equal_to(0));
+}
diff --git a/quantum/serial_link/tests/frame_validator_tests.c b/quantum/serial_link/tests/frame_validator_tests.c
new file mode 100644
index 0000000000..d20947e2c9
--- /dev/null
+++ b/quantum/serial_link/tests/frame_validator_tests.c
@@ -0,0 +1,101 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Fred Sundvik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include "serial_link/protocol/frame_validator.c"
+
+void route_incoming_frame(uint8_t link, uint8_t* data, uint16_t size) {
+ mock(data, size);
+}
+
+void byte_stuffer_send_frame(uint8_t link, uint8_t* data, uint16_t size) {
+ mock(data, size);
+}
+
+Describe(FrameValidator);
+BeforeEach(FrameValidator) {}
+AfterEach(FrameValidator) {}
+
+Ensure(FrameValidator, doesnt_validate_frames_under_5_bytes) {
+ never_expect(route_incoming_frame);
+ uint8_t data[] = {1, 2};
+ validator_recv_frame(0, 0, 1);
+ validator_recv_frame(0, data, 2);
+ validator_recv_frame(0, data, 3);
+ validator_recv_frame(0, data, 4);
+}
+
+Ensure(FrameValidator, validates_one_byte_frame_with_correct_crc) {
+ uint8_t data[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
+ expect(route_incoming_frame,
+ when(size, is_equal_to(1)),
+ when(data, is_equal_to_contents_of(data, 1))
+ );
+ validator_recv_frame(0, data, 5);
+}
+
+Ensure(FrameValidator, does_not_validate_one_byte_frame_with_incorrect_crc) {
+ uint8_t data[] = {0x44, 0, 0, 0, 0};
+ never_expect(route_incoming_frame);
+ validator_recv_frame(1, data, 5);
+}
+
+Ensure(FrameValidator, validates_four_byte_frame_with_correct_crc) {
+ uint8_t data[] = {0x44, 0x10, 0xFF, 0x00, 0x74, 0x4E, 0x30, 0xBA};
+ expect(route_incoming_frame,
+ when(size, is_equal_to(4)),
+ when(data, is_equal_to_contents_of(data, 4))
+ );
+ validator_recv_frame(1, data, 8);
+}
+
+Ensure(FrameValidator, validates_five_byte_frame_with_correct_crc) {
+ uint8_t data[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
+ expect(route_incoming_frame,
+ when(size, is_equal_to(5)),
+ when(data, is_equal_to_contents_of(data, 5))
+ );
+ validator_recv_frame(0, data, 9);
+}
+
+Ensure(FrameValidator, sends_one_byte_with_correct_crc) {
+ uint8_t original[] = {0x44, 0, 0, 0, 0};
+ uint8_t expected[] = {0x44, 0x04, 0x6A, 0xB3, 0xA3};
+ expect(byte_stuffer_send_frame,
+ when(size, is_equal_to(sizeof(expected))),
+ when(data, is_equal_to_contents_of(expected, sizeof(expected)))
+ );
+ validator_send_frame(0, original, 1);
+}
+
+Ensure(FrameValidator, sends_five_bytes_with_correct_crc) {
+ uint8_t original[] = {1, 2, 3, 4, 5, 0, 0, 0, 0};
+ uint8_t expected[] = {1, 2, 3, 4, 5, 0xF4, 0x99, 0x0B, 0x47};
+ expect(byte_stuffer_send_frame,
+ when(size, is_equal_to(sizeof(expected))),
+ when(data, is_equal_to_contents_of(expected, sizeof(expected)))
+ );
+ validator_send_frame(0, original, 5);
+}
diff --git a/quantum/serial_link/tests/transport_tests.c b/quantum/serial_link/tests/transport_tests.c
new file mode 100644
index 0000000000..358e1b9fd4
--- /dev/null
+++ b/quantum/serial_link/tests/transport_tests.c
@@ -0,0 +1,168 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Fred Sundvik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <cgreen/cgreen.h>
+#include <cgreen/mocks.h>
+#include "serial_link/protocol/transport.c"
+#include "serial_link/protocol/triple_buffered_object.c"
+
+void signal_data_written(void) {
+ mock();
+}
+
+static uint8_t sent_data[2048];
+static uint16_t sent_data_size;
+
+void router_send_frame(uint8_t destination, uint8_t* data, uint16_t size) {
+ mock(destination);
+ memcpy(sent_data + sent_data_size, data, size);
+ sent_data_size += size;
+}
+
+typedef struct {
+ uint32_t test;
+} test_object1_t;
+
+typedef struct {
+ uint32_t test1;
+ uint32_t test2;
+} test_object2_t;
+
+MASTER_TO_ALL_SLAVES_OBJECT(master_to_slave, test_object1_t);
+MASTER_TO_SINGLE_SLAVE_OBJECT(master_to_single_slave, test_object1_t);
+SLAVE_TO_MASTER_OBJECT(slave_to_master, test_object1_t);
+
+static remote_object_t* test_remote_objects[] = {
+ REMOTE_OBJECT(master_to_slave),
+ REMOTE_OBJECT(master_to_single_slave),
+ REMOTE_OBJECT(slave_to_master),
+};
+
+Describe(Transport);
+BeforeEach(Transport) {
+ add_remote_objects(test_remote_objects, sizeof(test_remote_objects) / sizeof(remote_object_t*));
+ sent_data_size = 0;
+}
+AfterEach(Transport) {}
+
+Ensure(Transport, write_to_local_signals_an_event) {
+ begin_write_master_to_slave();
+ expect(signal_data_written);
+ end_write_master_to_slave();
+ begin_write_slave_to_master();
+ expect(signal_data_written);
+ end_write_slave_to_master();
+ begin_write_master_to_single_slave(1);
+ expect(signal_data_written);
+ end_write_master_to_single_slave(1);
+}
+
+Ensure(Transport, writes_from_master_to_all_slaves) {
+ update_transport();
+ test_object1_t* obj = begin_write_master_to_slave();
+ obj->test = 5;
+ expect(signal_data_written);
+ end_write_master_to_slave();
+ expect(router_send_frame,
+ when(destination, is_equal_to(0xFF)));
+ update_transport();
+ transport_recv_frame(0, sent_data, sent_data_size);
+ test_object1_t* obj2 = read_master_to_slave();
+ assert_that(obj2, is_not_equal_to(NULL));
+ assert_that(obj2->test, is_equal_to(5));
+}
+
+Ensure(Transport, writes_from_slave_to_master) {
+ update_transport();
+ test_object1_t* obj = begin_write_slave_to_master();
+ obj->test = 7;
+ expect(signal_data_written);
+ end_write_slave_to_master();
+ expect(router_send_frame,
+ when(destination, is_equal_to(0)));
+ update_transport();
+ transport_recv_frame(3, sent_data, sent_data_size);
+ test_object1_t* obj2 = read_slave_to_master(2);
+ assert_that(read_slave_to_master(0), is_equal_to(NULL));
+ assert_that(obj2, is_not_equal_to(NULL));
+ assert_that(obj2->test, is_equal_to(7));
+}
+
+Ensure(Transport, writes_from_master_to_single_slave) {
+ update_transport();
+ test_object1_t* obj = begin_write_master_to_single_slave(3);
+ obj->test = 7;
+ expect(signal_data_written);
+ end_write_master_to_single_slave(3);
+ expect(router_send_frame,
+ when(destination, is_equal_to(4)));
+ update_transport();
+ transport_recv_frame(0, sent_data, sent_data_size);
+ test_object1_t* obj2 = read_master_to_single_slave();
+ assert_that(obj2, is_not_equal_to(NULL));
+ assert_that(obj2->test, is_equal_to(7));
+}
+
+Ensure(Transport, ignores_object_with_invalid_id) {
+ update_transport();
+ test_object1_t* obj = begin_write_master_to_single_slave(3);
+ obj->test = 7;
+ expect(signal_data_written);
+ end_write_master_to_single_slave(3);
+ expect(router_send_frame,
+ when(destination, is_equal_to(4)));
+ update_transport();
+ sent_data[sent_data_size - 1] = 44;
+ transport_recv_frame(0, sent_data, sent_data_size);
+ test_object1_t* obj2 = read_master_to_single_slave();
+ assert_that(obj2, is_equal_to(NULL));
+}
+
+Ensure(Transport, ignores_object_with_size_too_small) {
+ update_transport();
+ test_object1_t* obj = begin_write_master_to_slave();
+ obj->test = 7;
+ expect(signal_data_written);
+ end_write_master_to_slave();
+ expect(router_send_frame);
+ update_transport();
+ sent_data[sent_data_size - 2] = 0;
+ transport_recv_frame(0, sent_data, sent_data_size - 1);
+ test_object1_t* obj2 = read_master_to_slave();
+ assert_that(obj2, is_equal_to(NULL));
+}
+
+Ensure(Transport, ignores_object_with_size_too_big) {
+ update_transport();
+ test_object1_t* obj = begin_write_master_to_slave();
+ obj->test = 7;
+ expect(signal_data_written);
+ end_write_master_to_slave();
+ expect(router_send_frame);
+ update_transport();
+ sent_data[sent_data_size + 21] = 0;
+ transport_recv_frame(0, sent_data, sent_data_size + 22);
+ test_object1_t* obj2 = read_master_to_slave();
+ assert_that(obj2, is_equal_to(NULL));
+}
diff --git a/quantum/serial_link/tests/triple_buffered_object_tests.c b/quantum/serial_link/tests/triple_buffered_object_tests.c
new file mode 100644
index 0000000000..6f7c82b468
--- /dev/null
+++ b/quantum/serial_link/tests/triple_buffered_object_tests.c
@@ -0,0 +1,82 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2016 Fred Sundvik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+#include <cgreen/cgreen.h>
+#include "serial_link/protocol/triple_buffered_object.c"
+
+typedef struct {
+ uint8_t state;
+ uint32_t buffer[3];
+}test_object_t;
+
+test_object_t test_object;
+
+Describe(TripleBufferedObject);
+BeforeEach(TripleBufferedObject) {
+ triple_buffer_init((triple_buffer_object_t*)&test_object);
+}
+AfterEach(TripleBufferedObject) {}
+
+
+Ensure(TripleBufferedObject, writes_and_reads_object) {
+ *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
+ triple_buffer_end_write(&test_object);
+ assert_that(*triple_buffer_read(&test_object), is_equal_to(0x3456ABCC));
+}
+
+Ensure(TripleBufferedObject, does_not_read_empty) {
+ assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
+}
+
+Ensure(TripleBufferedObject, writes_twice_and_reads_object) {
+ *triple_buffer_begin_write(&test_object) = 0x3456ABCC;
+ triple_buffer_end_write(&test_object);
+ *triple_buffer_begin_write(&test_object) = 0x44778899;
+ triple_buffer_end_write(&test_object);
+ assert_that(*triple_buffer_read(&test_object), is_equal_to(0x44778899));
+}
+
+Ensure(TripleBufferedObject, performs_another_write_in_the_middle_of_read) {
+ *triple_buffer_begin_write(&test_object) = 1;
+ triple_buffer_end_write(&test_object);
+ uint32_t* read = triple_buffer_read(&test_object);
+ *triple_buffer_begin_write(&test_object) = 2;
+ triple_buffer_end_write(&test_object);
+ assert_that(*read, is_equal_to(1));
+ assert_that(*triple_buffer_read(&test_object), is_equal_to(2));
+ assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
+}
+
+Ensure(TripleBufferedObject, performs_two_writes_in_the_middle_of_read) {
+ *triple_buffer_begin_write(&test_object) = 1;
+ triple_buffer_end_write(&test_object);
+ uint32_t* read = triple_buffer_read(&test_object);
+ *triple_buffer_begin_write(&test_object) = 2;
+ triple_buffer_end_write(&test_object);
+ *triple_buffer_begin_write(&test_object) = 3;
+ triple_buffer_end_write(&test_object);
+ assert_that(*read, is_equal_to(1));
+ assert_that(*triple_buffer_read(&test_object), is_equal_to(3));
+ assert_that(triple_buffer_read(&test_object), is_equal_to(NULL));
+}