summaryrefslogtreecommitdiff
path: root/deps/fff/examples/embedded_ui
diff options
context:
space:
mode:
Diffstat (limited to 'deps/fff/examples/embedded_ui')
-rw-r--r--deps/fff/examples/embedded_ui/CMakeLists.txt23
-rw-r--r--deps/fff/examples/embedded_ui/Kata.txt25
-rw-r--r--deps/fff/examples/embedded_ui/include/DISPLAY.h17
-rw-r--r--deps/fff/examples/embedded_ui/include/SYSTEM.h21
-rw-r--r--deps/fff/examples/embedded_ui/include/UI.h12
-rw-r--r--deps/fff/examples/embedded_ui/src/UI.c48
-rw-r--r--deps/fff/examples/embedded_ui/src/UI_test_ansic.c183
-rw-r--r--deps/fff/examples/embedded_ui/src/UI_test_cpp.cpp136
8 files changed, 465 insertions, 0 deletions
diff --git a/deps/fff/examples/embedded_ui/CMakeLists.txt b/deps/fff/examples/embedded_ui/CMakeLists.txt
new file mode 100644
index 0000000..d2f3015
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright 2022 Google LLC
+# SPDX-License-Identifier: Apache-2.0
+
+# Create the ui_test_ansic test binary
+add_executable(ui_test_ansic src/UI_test_ansic.c src/UI.c)
+target_include_directories(ui_test_ansic PRIVATE include)
+target_link_libraries(ui_test_ansic PRIVATE fff)
+
+# Create the ui_test_cpp test binary
+add_executable(ui_test_cpp src/UI_test_cpp.cpp src/UI.c)
+target_include_directories(ui_test_cpp PRIVATE include)
+target_link_libraries(ui_test_cpp PRIVATE GTest::gtest_main fff)
+
+# Add tests to ctest
+add_test(
+ NAME ui_test_ansic
+ COMMAND $<TARGET_FILE:ui_test_ansic>
+)
+
+add_test(
+ NAME ui_test_cpp
+ COMMAND $<TARGET_FILE:ui_test_cpp>
+)
diff --git a/deps/fff/examples/embedded_ui/Kata.txt b/deps/fff/examples/embedded_ui/Kata.txt
new file mode 100644
index 0000000..b466c36
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/Kata.txt
@@ -0,0 +1,25 @@
+
+Problem Definition
+------------------
+The task is to write a user interface module for an embedded device.
+
+Interrupts:
+ * The user interface is responsible for initializing the display.
+ * The user interface will register an interrupt handler for GPIO input 2 (a
+ push button).
+ * It will be possible to register a callback function for button presses.
+ * When there is no callback function set the irq handler will increment a
+ missed irq counter.
+ * When the interrupt occurs the handler will schedule or execute the button
+ press callback if there is one registered.
+Output:
+ * Tasks can write messages to the user interface to be output on the display.
+ * The display is line oriented; when the last line of the display is written
+ the user interface is responsible for clearing the display.
+ * The display is 26 characters wide. Any string longer than that must be
+ truncated before being sent to the display. The string must be null
+ terminated and thus maximum 27 bytes long.
+
+ * BONUS: Have the display be scrolling, i.e. when the display is full, the
+ previous lines must be shifted up one and the new line written in the bottom
+ line.
diff --git a/deps/fff/examples/embedded_ui/include/DISPLAY.h b/deps/fff/examples/embedded_ui/include/DISPLAY.h
new file mode 100644
index 0000000..45ca62e
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/include/DISPLAY.h
@@ -0,0 +1,17 @@
+/*
+ * DISPLAY.h
+ *
+ * Created on: Dec 17, 2010
+ * Author: mlong
+ */
+
+#ifndef DISPLAY_H_
+#define DISPLAY_H_
+
+void DISPLAY_init();
+void DISPLAY_clear();
+unsigned int DISPLAY_get_line_capacity();
+unsigned int DISPLAY_get_line_insert_index();
+void DISPLAY_output(char * message);
+
+#endif /* DISPLAY_H_ */
diff --git a/deps/fff/examples/embedded_ui/include/SYSTEM.h b/deps/fff/examples/embedded_ui/include/SYSTEM.h
new file mode 100644
index 0000000..080144f
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/include/SYSTEM.h
@@ -0,0 +1,21 @@
+/*
+ * DISPLAY.h
+ *
+ * Created on: Dec 17, 2010
+ * Author: mlong
+ */
+
+#ifndef SYSTEM_H_
+#define SYSTEM_H_
+
+typedef void (*irq_func_t)(void);
+
+#define IRQ_GPIO_0 0x70
+#define IRQ_GPIO_1 0x71
+#define IRQ_GPIO_2 0x72
+#define IRQ_GPIO_3 0x73
+
+
+void SYSTEM_register_irq(irq_func_t, unsigned int irq);
+
+#endif /* SYSTEM_H_ */
diff --git a/deps/fff/examples/embedded_ui/include/UI.h b/deps/fff/examples/embedded_ui/include/UI.h
new file mode 100644
index 0000000..8a3fb5c
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/include/UI.h
@@ -0,0 +1,12 @@
+#ifndef UI_H_
+#define UI_H_
+
+typedef void (*button_cbk_t)(void);
+
+void UI_init();
+unsigned int UI_get_missed_irqs();
+void UI_button_irq_handler();
+void UI_register_button_cbk(button_cbk_t cbk);
+void UI_write_line(char *line);
+
+#endif /* UI_H_ */
diff --git a/deps/fff/examples/embedded_ui/src/UI.c b/deps/fff/examples/embedded_ui/src/UI.c
new file mode 100644
index 0000000..8ce996e
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/src/UI.c
@@ -0,0 +1,48 @@
+#include "UI.h"
+#include "DISPLAY.h"
+#include "SYSTEM.h"
+#include <string.h>
+
+static unsigned int missed_irq_counter;
+button_cbk_t button_cbk;
+
+
+void UI_init()
+{
+ DISPLAY_init();
+ SYSTEM_register_irq(UI_button_irq_handler, IRQ_GPIO_2);
+ button_cbk = 0;
+ missed_irq_counter = 0;
+}
+
+unsigned int UI_get_missed_irqs()
+{
+ return missed_irq_counter;
+}
+
+void UI_button_irq_handler()
+{
+ if(button_cbk)
+ {
+ button_cbk();
+ }
+ else
+ {
+ missed_irq_counter++;
+ }
+}
+
+void UI_register_button_cbk(button_cbk_t cbk)
+{
+ button_cbk = cbk;
+}
+
+void UI_write_line(char *line)
+{
+ static char out[27];
+ strncpy(out, line, 26);
+ out[26] = '\0';
+ if(DISPLAY_get_line_capacity() == DISPLAY_get_line_insert_index())
+ DISPLAY_clear();
+ DISPLAY_output(out);
+}
diff --git a/deps/fff/examples/embedded_ui/src/UI_test_ansic.c b/deps/fff/examples/embedded_ui/src/UI_test_ansic.c
new file mode 100644
index 0000000..3e3fce9
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/src/UI_test_ansic.c
@@ -0,0 +1,183 @@
+#include "UI.h"
+#include "fff.h"
+#include "SYSTEM.h"
+#include "DISPLAY.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Test Framework :-) */
+void setup();
+#define TEST_F(SUITE, NAME) void NAME()
+#define RUN_TEST(SUITE, TESTNAME) printf(" Running %s.%s: \n", #SUITE, #TESTNAME); setup(); TESTNAME(); printf(" SUCCESS\n");
+
+DEFINE_FFF_GLOBALS;
+
+/* SYSTEM.h */
+FAKE_VOID_FUNC2(SYSTEM_register_irq, irq_func_t, unsigned int);
+/* DISPLAY.h */
+FAKE_VOID_FUNC(DISPLAY_init);
+FAKE_VOID_FUNC(DISPLAY_clear);
+FAKE_VOID_FUNC(DISPLAY_output, char *);
+FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_capacity);
+FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_insert_index);
+
+FAKE_VOID_FUNC0(button_press_cbk);
+
+
+
+/* Initialializers called for every test */
+void setup()
+{
+ RESET_FAKE(SYSTEM_register_irq);
+
+ RESET_FAKE(DISPLAY_init)
+ RESET_FAKE(DISPLAY_clear)
+ RESET_FAKE(DISPLAY_output)
+ RESET_FAKE(DISPLAY_get_line_capacity)
+ RESET_FAKE(DISPLAY_get_line_insert_index);
+
+ RESET_FAKE(button_press_cbk);
+
+ FFF_RESET_HISTORY();
+
+ DISPLAY_get_line_capacity_fake.return_val = 2;
+}
+
+/* Tests go here */
+TEST_F(UITests, init_will_initialise_display)
+{
+ UI_init();
+ assert(DISPLAY_init_fake.call_count == 1);
+}
+
+TEST_F(UITests, init_will_register_interrupt_gpio2)
+{
+ UI_init();
+ assert(SYSTEM_register_irq_fake.call_count == 1);
+ assert(SYSTEM_register_irq_fake.arg0_val == UI_button_irq_handler);
+ assert(SYSTEM_register_irq_fake.arg1_val == IRQ_GPIO_2);
+}
+
+TEST_F(UITests, when_no_irq_then_missed_irq_counter_zero)
+{
+ assert(UI_get_missed_irqs() == 0);
+}
+
+TEST_F(UITests, when_one_irq_and_no_handler_then_missed_irq_counter_one)
+{
+ UI_button_irq_handler();
+ assert(UI_get_missed_irqs() == 1);
+}
+
+TEST_F(UITests, when_one_irq_and_valid_callback_then_missed_irq_counter_zero)
+{
+ UI_init();
+ UI_register_button_cbk(button_press_cbk);
+ UI_button_irq_handler();
+ assert(UI_get_missed_irqs() == 0);
+}
+
+TEST_F(UITests, when_one_irq_and_valid_callback_then_callback_called)
+{
+ UI_register_button_cbk(button_press_cbk);
+ UI_button_irq_handler();
+ assert(button_press_cbk_fake.call_count == 1);
+}
+
+TEST_F(UITests, write_line_outputs_lines_to_display)
+{
+ char msg[] = "helloworld";
+ UI_write_line(msg);
+ assert(DISPLAY_output_fake.call_count == 1);
+ assert(strncmp(DISPLAY_output_fake.arg0_val, msg, 26) == 0);
+}
+
+TEST_F(UITests, when_no_empty_lines_write_line_clears_screen_and_outputs_lines_to_display)
+{
+ DISPLAY_get_line_insert_index_fake.return_val = 2;
+ char msg[] = "helloworld";
+
+ UI_write_line(msg);
+
+ assert(DISPLAY_clear_fake.call_count == 1);
+ assert(DISPLAY_output_fake.call_count == 1);
+ // Check the order of the calls: Don't care about the first two:
+ // DISPLAY_get_line_capacity and DISPLAY_get_line_insert_index
+ assert(fff.call_history_idx == 4);
+ assert(fff.call_history[2] == (void *) DISPLAY_clear);
+ assert(fff.call_history[3] == (void *) DISPLAY_output);
+}
+
+TEST_F(UITests, when_empty_lines_write_line_doesnt_clear_screen)
+{
+ // given
+ DISPLAY_get_line_insert_index_fake.return_val = 1;
+ char msg[] = "helloworld";
+ // when
+ UI_write_line(msg);
+ // then
+ assert(DISPLAY_clear_fake.call_count == 0);
+}
+
+TEST_F(UITests, when_string_longer_than_26_then_truncated_string_output)
+{
+ // given
+ char input[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char expected[] = "abcdefghijklmnopqrstuvwxyz";
+ // when
+ UI_write_line(input);
+ // then
+ assert(strncmp(expected, DISPLAY_output_fake.arg0_val, 37) == 0);
+}
+
+TEST_F(UITests, when_outputting_to_full_display_then_previous_inserted)
+{
+ // given
+ DISPLAY_get_line_insert_index_fake.return_val = 1;
+ char oldest[] = "oldest";
+ char newest[] = "newest";
+ // when
+ UI_write_line(oldest);
+ UI_write_line(newest);
+ // then
+
+ assert(DISPLAY_output_fake.call_count == 2);
+
+ // fills last line
+ assert(strncmp(oldest, DISPLAY_output_fake.arg0_history[0], 37) == 0);
+ //clears
+ assert(DISPLAY_clear_fake.call_count == 1);
+ // inserts old line at first
+ assert(strncmp(oldest, DISPLAY_output_fake.arg0_history[1], 37) == 0);
+ // then inserts new line
+ assert(strncmp(newest, DISPLAY_output_fake.arg0_history[2], 37) == 0);
+}
+
+int main()
+{
+ setbuf(stdout, NULL);
+ fprintf(stdout, "-------------\n");
+ fprintf(stdout, "Running Tests\n");
+ fprintf(stdout, "-------------\n\n");
+ fflush(0);
+
+ /* Run tests */
+ RUN_TEST(UITests, init_will_initialise_display);
+ RUN_TEST(UITests, init_will_register_interrupt_gpio2);
+ RUN_TEST(UITests, when_no_irq_then_missed_irq_counter_zero);
+ RUN_TEST(UITests, when_one_irq_and_no_handler_then_missed_irq_counter_one);
+ RUN_TEST(UITests, when_one_irq_and_valid_callback_then_missed_irq_counter_zero);
+ RUN_TEST(UITests, when_one_irq_and_valid_callback_then_callback_called);
+ RUN_TEST(UITests, write_line_outputs_lines_to_display);
+ RUN_TEST(UITests, when_no_empty_lines_write_line_clears_screen_and_outputs_lines_to_display);
+ RUN_TEST(UITests, when_empty_lines_write_line_doesnt_clear_screen);
+ RUN_TEST(UITests, when_string_longer_than_26_then_truncated_string_output);
+
+ printf("\n-------------\n");
+ printf("Complete\n");
+ printf("-------------\n\n");
+
+ return 0;
+}
diff --git a/deps/fff/examples/embedded_ui/src/UI_test_cpp.cpp b/deps/fff/examples/embedded_ui/src/UI_test_cpp.cpp
new file mode 100644
index 0000000..e7d9bba
--- /dev/null
+++ b/deps/fff/examples/embedded_ui/src/UI_test_cpp.cpp
@@ -0,0 +1,136 @@
+extern "C"{
+#include "UI.h"
+#include "SYSTEM.h"
+#include "DISPLAY.h"
+}
+#include <gtest/gtest.h>
+
+
+#include "fff.h"
+DEFINE_FFF_GLOBALS;
+
+/* SYSTEM.h */
+FAKE_VOID_FUNC(SYSTEM_register_irq, irq_func_t, unsigned int);
+/* DISPLAY.h */
+FAKE_VOID_FUNC(DISPLAY_init);
+FAKE_VOID_FUNC(DISPLAY_clear);
+FAKE_VOID_FUNC(DISPLAY_output, char *);
+FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_capacity);
+FAKE_VALUE_FUNC(unsigned int, DISPLAY_get_line_insert_index);
+
+FAKE_VOID_FUNC(button_press_cbk);
+
+
+class UITests : public testing::Test
+{
+public:
+
+ void SetUp()
+ {
+ // Register resets
+ RESET_FAKE(SYSTEM_register_irq);
+
+ RESET_FAKE(DISPLAY_init)
+ RESET_FAKE(DISPLAY_clear)
+ RESET_FAKE(DISPLAY_output)
+ RESET_FAKE(DISPLAY_get_line_capacity)
+ RESET_FAKE(DISPLAY_get_line_insert_index);
+
+ RESET_FAKE(button_press_cbk);
+
+ FFF_RESET_HISTORY();
+ // non default init
+ DISPLAY_get_line_capacity_fake.return_val = 2;
+ }
+};
+
+
+
+/* Tests go here */
+TEST_F(UITests, init_will_initialise_display)
+{
+ UI_init();
+ ASSERT_EQ(DISPLAY_init_fake.call_count, 1);
+}
+
+TEST_F(UITests, init_will_register_interrupt_gpio2)
+{
+ UI_init();
+ ASSERT_EQ(SYSTEM_register_irq_fake.call_count, 1);
+ ASSERT_EQ((void *)SYSTEM_register_irq_fake.arg0_val, (void *)UI_button_irq_handler);
+ ASSERT_EQ(SYSTEM_register_irq_fake.arg1_val, IRQ_GPIO_2);
+}
+
+TEST_F(UITests, when_no_irq_then_missed_irq_counter_zero)
+{
+ ASSERT_EQ(UI_get_missed_irqs(), 0);
+}
+
+TEST_F(UITests, when_one_irq_and_no_handler_then_missed_irq_counter_one)
+{
+ UI_button_irq_handler();
+ ASSERT_EQ(UI_get_missed_irqs(), 1);
+}
+
+TEST_F(UITests, when_one_irq_and_valid_callback_then_missed_irq_counter_zero)
+{
+ UI_init();
+ UI_register_button_cbk(button_press_cbk);
+ UI_button_irq_handler();
+ ASSERT_EQ(UI_get_missed_irqs(), 0);
+}
+
+TEST_F(UITests, when_one_irq_and_valid_callback_then_callback_called)
+{
+ UI_register_button_cbk(button_press_cbk);
+ UI_button_irq_handler();
+ ASSERT_EQ(button_press_cbk_fake.call_count, 1);
+}
+
+TEST_F(UITests, write_line_outputs_lines_to_display)
+{
+ char msg[] = "helloworld";
+ UI_write_line(msg);
+ ASSERT_EQ(DISPLAY_output_fake.call_count, 1);
+ ASSERT_EQ(strncmp(DISPLAY_output_fake.arg0_val, msg, 26), 0);
+}
+
+TEST_F(UITests, when_no_empty_lines_write_line_clears_screen_and_outputs_lines_to_display)
+{
+ DISPLAY_get_line_insert_index_fake.return_val = 2;
+ char msg[] = "helloworld";
+
+ UI_write_line(msg);
+
+ ASSERT_EQ(DISPLAY_clear_fake.call_count, 1);
+ ASSERT_EQ(DISPLAY_output_fake.call_count, 1);
+ // Check the order of the calls: Don't care about the first two:
+ // DISPLAY_get_line_capacity and DISPLAY_get_line_insert_index
+ ASSERT_EQ(fff.call_history_idx, 4);
+ ASSERT_EQ(fff.call_history[2], (void *) DISPLAY_clear);
+ ASSERT_EQ(fff.call_history[3], (void *) DISPLAY_output);
+}
+
+TEST_F(UITests, when_empty_lines_write_line_doesnt_clear_screen)
+{
+ // given
+ DISPLAY_get_line_insert_index_fake.return_val = 1;
+ char msg[] = "helloworld";
+ // when
+ UI_write_line(msg);
+ // then
+ ASSERT_EQ(DISPLAY_clear_fake.call_count, 0);
+}
+
+TEST_F(UITests, when_string_longer_than_26_then_truncated_string_output)
+{
+ // given
+ char input[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char expected[] = "abcdefghijklmnopqrstuvwxyz";
+ // when
+ UI_write_line(input);
+ // then
+ ASSERT_EQ(strncmp(expected, DISPLAY_output_fake.arg0_val, 37), 0);
+}
+
+