diff options
Diffstat (limited to 'deps/fff/examples/embedded_ui')
-rw-r--r-- | deps/fff/examples/embedded_ui/CMakeLists.txt | 23 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/Kata.txt | 25 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/include/DISPLAY.h | 17 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/include/SYSTEM.h | 21 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/include/UI.h | 12 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/src/UI.c | 48 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/src/UI_test_ansic.c | 183 | ||||
-rw-r--r-- | deps/fff/examples/embedded_ui/src/UI_test_cpp.cpp | 136 |
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); +} + + |