diff options
Diffstat (limited to 'deps/fff/examples')
40 files changed, 1071 insertions, 0 deletions
diff --git a/deps/fff/examples/CMakeLists.txt b/deps/fff/examples/CMakeLists.txt new file mode 100644 index 0000000..58a3d09 --- /dev/null +++ b/deps/fff/examples/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright 2022 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory(driver_testing) +add_subdirectory(embedded_ui) +add_subdirectory(weak_linking) diff --git a/deps/fff/examples/driver_testing/CMakeLists.txt b/deps/fff/examples/driver_testing/CMakeLists.txt new file mode 100644 index 0000000..3580349 --- /dev/null +++ b/deps/fff/examples/driver_testing/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright 2022 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +# Create the driver test binary +add_executable(driver_test + src/driver.c + src/driver.test.cpp +) +target_include_directories(driver_test PRIVATE include) +target_link_libraries(driver_test PRIVATE GTest::gtest_main fff) +target_compile_definitions(driver_test PUBLIC TEST_USER_OWN_TR1_TUPLE=1 TESTING) + +# Create the driver fff test binary +add_executable(driver_fff_test + src/driver.c + src/driver.test.fff.cpp +) +target_include_directories(driver_fff_test PRIVATE include) +target_link_libraries(driver_fff_test PRIVATE GTest::gtest_main fff) +target_compile_definitions(driver_fff_test PUBLIC TEST_USER_OWN_TR1_TUPLE=1 TESTING) + +# Add tests to ctest +add_test( + NAME driver_test + COMMAND $<TARGET_FILE:driver_test> +) + +add_test( + NAME driver_fff_test + COMMAND $<TARGET_FILE:driver_fff_test> +) diff --git a/deps/fff/examples/driver_testing/include/driver.h b/deps/fff/examples/driver_testing/include/driver.h new file mode 100644 index 0000000..b7406d4 --- /dev/null +++ b/deps/fff/examples/driver_testing/include/driver.h @@ -0,0 +1,11 @@ + +#ifndef DRIVER +#define DRIVER + +#include <stdint.h> + +void driver_write(uint8_t val); +uint8_t driver_read(); +void driver_init_device(); + +#endif /*include guard*/ diff --git a/deps/fff/examples/driver_testing/include/hardware_abstraction.h b/deps/fff/examples/driver_testing/include/hardware_abstraction.h new file mode 100644 index 0000000..affa92e --- /dev/null +++ b/deps/fff/examples/driver_testing/include/hardware_abstraction.h @@ -0,0 +1,15 @@ +#ifndef HARDWARE_ABSTRACTION +#define HARDWARE_ABSTRACTION + +#include <stdint.h> + +#ifndef TESTING +#define IO_MEM_RD8(ADDR) (*((volatile uint8_t *)(ADDR))) +#define IO_MEM_WR8(ADDR, VAL_8) (*((volatile uint8_t *)(ADDR)) = (VAL_8)) +#else +/* In testing use fake functions to record calls to IO memory */ +uint8_t IO_MEM_RD8(uint32_t reg); +void IO_MEM_WR8(uint32_t reg, uint8_t val); +#endif + +#endif /* Include guard */ diff --git a/deps/fff/examples/driver_testing/include/registers.h b/deps/fff/examples/driver_testing/include/registers.h new file mode 100644 index 0000000..5c9e5a9 --- /dev/null +++ b/deps/fff/examples/driver_testing/include/registers.h @@ -0,0 +1,13 @@ +#ifndef REGISTERS_H_ +#define REGISTERS_H_ + +#define DRIVER_OUTPUT_REGISTER 0xFFAAu +#define DRIVER_INPUT_REGISTER 0XFFABu +#define DRIVER_PERIPHERAL_ENABLE_REG 0xFFACu +#define DRIVER_PERIPHERAL_INITIALIZE_REG 0xFFACu + +#define HARDWARE_VERSION_REGISTER 0xFF00u +#define HARDWARE_REV_A 0x00u +#define HARDWARE_REV_B 0x01u + +#endif /* REGISTERS_H_ */ diff --git a/deps/fff/examples/driver_testing/src/driver.c b/deps/fff/examples/driver_testing/src/driver.c new file mode 100644 index 0000000..9454ba6 --- /dev/null +++ b/deps/fff/examples/driver_testing/src/driver.c @@ -0,0 +1,24 @@ + + +#include "hardware_abstraction.h" +#include "registers.h" + +void driver_write(uint8_t val) +{ + IO_MEM_WR8(DRIVER_OUTPUT_REGISTER, val); +} + +uint8_t driver_read() +{ + return IO_MEM_RD8(DRIVER_INPUT_REGISTER); +} + +void driver_init_device() +{ + uint8_t hw_version = IO_MEM_RD8(HARDWARE_VERSION_REGISTER); + if(HARDWARE_REV_B == hw_version) + { + IO_MEM_WR8(DRIVER_PERIPHERAL_ENABLE_REG, 1); + } + IO_MEM_WR8(DRIVER_PERIPHERAL_INITIALIZE_REG, 1); +} diff --git a/deps/fff/examples/driver_testing/src/driver.test.cpp b/deps/fff/examples/driver_testing/src/driver.test.cpp new file mode 100644 index 0000000..cd80bb3 --- /dev/null +++ b/deps/fff/examples/driver_testing/src/driver.test.cpp @@ -0,0 +1,50 @@ +extern "C" +{ +#include "driver.h" +#include "registers.h" +} +#include "../../../fff.h" +#include <gtest/gtest.h> + + +extern "C" +{ + static uint8_t readVal; + static int readCalled; + static uint32_t readRegister; + uint8_t IO_MEM_RD8(uint32_t reg) + { + readRegister = reg; + readCalled++; + return readVal; + } + + static uint32_t writeRegister; + static uint8_t writeVal; + static int writeCalled; + void IO_MEM_WR8(uint32_t reg, uint8_t val) + { + writeRegister = reg; + writeVal = val; + writeCalled++; + } +} + +TEST(Driver, When_writing_Then_writes_data_to_DRIVER_OUTPUT_REGISTER) +{ + driver_write(0x34); + ASSERT_EQ(1u, writeCalled); + ASSERT_EQ(0x34u, writeVal); + ASSERT_EQ(DRIVER_OUTPUT_REGISTER, writeRegister); +} + + +TEST(Driver, When_reading_data_Then_reads_from_DRIVER_INPUT_REGISTER) +{ + readVal = 0x55; + uint8_t returnedValue = driver_read(); + ASSERT_EQ(1u, readCalled); + ASSERT_EQ(0x55u, returnedValue); + ASSERT_EQ(readRegister, DRIVER_INPUT_REGISTER); +} + diff --git a/deps/fff/examples/driver_testing/src/driver.test.fff.cpp b/deps/fff/examples/driver_testing/src/driver.test.fff.cpp new file mode 100644 index 0000000..56f6192 --- /dev/null +++ b/deps/fff/examples/driver_testing/src/driver.test.fff.cpp @@ -0,0 +1,63 @@ +extern "C"{ + #include "driver.h" + #include "registers.h" + #include "hardware_abstraction.h" +} +#include "fff.h" +#include <gtest/gtest.h> + +DEFINE_FFF_GLOBALS; + +FAKE_VOID_FUNC(IO_MEM_WR8, uint32_t, uint8_t); +FAKE_VALUE_FUNC(uint8_t, IO_MEM_RD8, uint32_t); + +class DriverTestFFF : public testing::Test +{ +public: + void SetUp() + { + RESET_FAKE(IO_MEM_WR8); + RESET_FAKE(IO_MEM_RD8); + FFF_RESET_HISTORY(); + } + +}; + +TEST_F(DriverTestFFF, When_writing_Then_writes_data_to_DRIVER_OUTPUT_REGISTER) +{ + driver_write(0x34); + ASSERT_EQ(1u, IO_MEM_WR8_fake.call_count); + ASSERT_EQ(0x34u, IO_MEM_WR8_fake.arg1_val); + ASSERT_EQ(DRIVER_OUTPUT_REGISTER, IO_MEM_WR8_fake.arg0_val); +} + + +TEST_F(DriverTestFFF, When_reading_data_Then_reads_from_DRIVER_INPUT_REGISTER) +{ + IO_MEM_RD8_fake.return_val = 0x55; + uint8_t returnedValue = driver_read(); + ASSERT_EQ(1u, IO_MEM_RD8_fake.call_count); + ASSERT_EQ(0x55u, returnedValue); + ASSERT_EQ(IO_MEM_RD8_fake.arg0_val, DRIVER_INPUT_REGISTER); +} + +TEST_F(DriverTestFFF, Given_revisionB_device_When_initialize_Then_enable_peripheral_before_initializing_it) +{ + // Given + IO_MEM_RD8_fake.return_val = HARDWARE_REV_B; + // When + driver_init_device(); + + //Then + // Gets the hardware revision + ASSERT_EQ((void*) IO_MEM_RD8, fff.call_history[0]); + ASSERT_EQ(HARDWARE_VERSION_REGISTER, IO_MEM_RD8_fake.arg0_history[0]); + // Enables Peripheral + ASSERT_EQ((void*) IO_MEM_WR8, fff.call_history[1]); + ASSERT_EQ(DRIVER_PERIPHERAL_ENABLE_REG, IO_MEM_WR8_fake.arg0_history[0]); + ASSERT_EQ(1, IO_MEM_WR8_fake.arg1_history[0]); + // Initializes Peripheral + ASSERT_EQ((void*) IO_MEM_WR8, fff.call_history[2]); + ASSERT_EQ(DRIVER_PERIPHERAL_INITIALIZE_REG,IO_MEM_WR8_fake.arg0_history[1]); + ASSERT_EQ(1, IO_MEM_WR8_fake.arg1_history[1]); +} 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); +} + + diff --git a/deps/fff/examples/weak_linking/CMakeLists.txt b/deps/fff/examples/weak_linking/CMakeLists.txt new file mode 100644 index 0000000..b1d5679 --- /dev/null +++ b/deps/fff/examples/weak_linking/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright 2022 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +# Skip these tests for Windows +if(WIN32) + message(STATUS "Weak linking requires __attribute__((weak)) which isn't supported on MS VS, skipping tests") + return() +endif() + +# Set the global FFF_GCC_FUNCTION_ATTRIBUTES for config.h +set(FFF_GCC_FUNCTION_ATTRIBUTES "__attribute__((weak))") +configure_file(config/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +# Create a libfakes static library that will be used in the executables below. +# This library will depend on the above generated config.h which will add the +# FFF 'weak' function attributes. +add_library(libfakes STATIC + test/src/bus.fake.c + test/src/display.fake.c + test/src/error.fake.c + test/src/sensor.fake.c + test/src/test_common.c +) +target_precompile_headers(libfakes PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/config.h) +target_include_directories(libfakes PUBLIC include test/include) +target_link_libraries(libfakes PUBLIC fff) + +# Create the main test binary +add_executable(test_main src/main.c test/src/main.test.c) +target_link_libraries(test_main PRIVATE libfakes) + +# Create the sensor test binary +add_executable(test_sensor src/sensor.c test/src/sensor.test.c) +target_link_libraries(test_sensor PRIVATE libfakes) + +# Create the display test binary +add_executable(test_display src/display.c test/src/display.test.c ${LIBFAKES_SRCS}) +target_link_libraries(test_display PRIVATE libfakes) + +# Add tests to ctest +add_test( + NAME test_main + COMMAND $<TARGET_FILE:test_main> +) + +add_test( + NAME test_sensor + COMMAND $<TARGET_FILE:test_sensor> +) + +add_test( + NAME test_display + COMMAND $<TARGET_FILE:test_display> +) diff --git a/deps/fff/examples/weak_linking/config/config.h.in b/deps/fff/examples/weak_linking/config/config.h.in new file mode 100644 index 0000000..30121ce --- /dev/null +++ b/deps/fff/examples/weak_linking/config/config.h.in @@ -0,0 +1,5 @@ +/* Copyright 2022 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +#cmakedefine FFF_GCC_FUNCTION_ATTRIBUTES @FFF_GCC_FUNCTION_ATTRIBUTES@ diff --git a/deps/fff/examples/weak_linking/include/bus.h b/deps/fff/examples/weak_linking/include/bus.h new file mode 100644 index 0000000..84a56cf --- /dev/null +++ b/deps/fff/examples/weak_linking/include/bus.h @@ -0,0 +1,6 @@ +#pragma once + +#include "error.h" + +bool bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo ); +bool bus_write( uint8_t dev, uint8_t registry, const uint8_t* buffer, int len, bool assume_echo ); diff --git a/deps/fff/examples/weak_linking/include/display.h b/deps/fff/examples/weak_linking/include/display.h new file mode 100644 index 0000000..da1ee1f --- /dev/null +++ b/deps/fff/examples/weak_linking/include/display.h @@ -0,0 +1,5 @@ +#pragma once +#include "error.h" + +bool display_init(); +void display_update( const char* info ); diff --git a/deps/fff/examples/weak_linking/include/error.h b/deps/fff/examples/weak_linking/include/error.h new file mode 100644 index 0000000..dd52269 --- /dev/null +++ b/deps/fff/examples/weak_linking/include/error.h @@ -0,0 +1,7 @@ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +void runtime_error( const char* msg ); +char* runtime_error_nice_print( const char* msg ); diff --git a/deps/fff/examples/weak_linking/include/sensor.h b/deps/fff/examples/weak_linking/include/sensor.h new file mode 100644 index 0000000..e199e45 --- /dev/null +++ b/deps/fff/examples/weak_linking/include/sensor.h @@ -0,0 +1,6 @@ +#pragma once + +#include "error.h" + +bool sensor_init(); +float sensor_read(); diff --git a/deps/fff/examples/weak_linking/src/bus.c b/deps/fff/examples/weak_linking/src/bus.c new file mode 100644 index 0000000..5706932 --- /dev/null +++ b/deps/fff/examples/weak_linking/src/bus.c @@ -0,0 +1,30 @@ +#pragma once + +uint16_t* BUS_REGISTER_BASE = (uint16_t*)0xDEADBEEF; + +typedef struct +{ + uint16_t action; + uint16_t reg; + uint8_t input; + uint8_t output +} BusDevice; + +BusDevice* BUS = (BusDevice*)BUS_REGISTER_BASE; + +bool bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo ) +{ + // Something that we dont want to run, since the emulation would be hard. + BUS->action = 0x01; + BUS->reg = registry; + for ( int loop =0 ; loop < len; loop ++ ) + { + char output = buffer[loop]; + BUS->output = output; + buffer[loop] = BUS->input; + + if ( ( assume_echo ) && ( buffer[loop] != output ) ) + return false; + } + return true; +} diff --git a/deps/fff/examples/weak_linking/src/display.c b/deps/fff/examples/weak_linking/src/display.c new file mode 100644 index 0000000..1b9a7d6 --- /dev/null +++ b/deps/fff/examples/weak_linking/src/display.c @@ -0,0 +1,29 @@ +#include <string.h> + +#include "display.h" +#include "bus.h" + +#define DISPLAY_ADDRESS 0xAF +#define DISPLAY_REG_INIT 0x10 +#define DISPLAY_REG_UPDATE 0x20 + +bool display_init() +{ + char init_config[] = { 0xDE, 0xFE, 0x00 }; + bus_read_write( DISPLAY_ADDRESS, DISPLAY_REG_INIT, (uint8_t*)init_config, 3, false ); + + if (init_config[2] != 0x10) + return false; + + return true; +} + +void display_update( const char* info ) +{ + int len = strlen( info ); + + if ( bus_write( DISPLAY_ADDRESS, DISPLAY_REG_UPDATE, (const uint8_t*)info, len, true ) != true ) + { + runtime_error("display update failed!"); + } +} diff --git a/deps/fff/examples/weak_linking/src/error.c b/deps/fff/examples/weak_linking/src/error.c new file mode 100644 index 0000000..5e7db63 --- /dev/null +++ b/deps/fff/examples/weak_linking/src/error.c @@ -0,0 +1,10 @@ +#include <assert.h> +#include "error.h" + +char* runtime_error_nice_print( const char* msg ) +{ + char* buffer = malloc(512); + snprintf(buffer, 512, "Got error: %s", msg ); + buffer[511] = 0; + return buffer; +} diff --git a/deps/fff/examples/weak_linking/src/main.c b/deps/fff/examples/weak_linking/src/main.c new file mode 100644 index 0000000..88be84c --- /dev/null +++ b/deps/fff/examples/weak_linking/src/main.c @@ -0,0 +1,27 @@ +#include <stdlib.h> +#include <stdio.h> + +#include "display.h" +#include "sensor.h" + +void give_error( const char* msg ) +{ + char* buffer = runtime_error_nice_print( msg ); + runtime_error( buffer ); + free(buffer); +} + +int update_main( void ) +{ + if ( !sensor_init() ) + runtime_error("sensor init failed"); + + if ( !display_init() ) + runtime_error("display init failed"); + + char buffer[32]; + float reading = sensor_read(); + snprintf( buffer, 32, "Sensor: %0.1f", reading ); + display_update( buffer ); + return 0; +} diff --git a/deps/fff/examples/weak_linking/src/sensor.c b/deps/fff/examples/weak_linking/src/sensor.c new file mode 100644 index 0000000..bff6642 --- /dev/null +++ b/deps/fff/examples/weak_linking/src/sensor.c @@ -0,0 +1,29 @@ +#include <string.h> + +#include "sensor.h" +#include "bus.h" + +#define SENSOR_ADDRESS 0xAA +#define SENSOR_REG_READ 0xF0 + +float LOCAL_buffer[4]; + +bool sensor_init() +{ + memset( LOCAL_buffer, 0x00, sizeof(LOCAL_buffer)); + return true; +} + +float sensor_read() +{ + char data[4] = { 0xFF, 0xFF, 0x00, 0x00 }; + bus_read_write( SENSOR_ADDRESS, SENSOR_REG_READ, (uint8_t*)&data, 4, false ); + + if (data[0] != 0x10) + { + runtime_error("sensor read failed"); + } + + float ret_value = data[1] + data[2] / 256.0; + return ret_value; +} diff --git a/deps/fff/examples/weak_linking/test/include/bus.fake.h b/deps/fff/examples/weak_linking/test/include/bus.fake.h new file mode 100644 index 0000000..170e45a --- /dev/null +++ b/deps/fff/examples/weak_linking/test/include/bus.fake.h @@ -0,0 +1,10 @@ +#ifndef _AUTOFAKE_BUS_H +#define _AUTOFAKE_BUS_H + +#include "fff.h" +#include "bus.h" + +DECLARE_FAKE_VALUE_FUNC( bool, bus_read_write, uint8_t, uint8_t, uint8_t*, int, bool ); +DECLARE_FAKE_VALUE_FUNC( bool, bus_write, uint8_t, uint8_t, const uint8_t*, int, bool ); + +#endif // _AUTOFAKE_BUS_H diff --git a/deps/fff/examples/weak_linking/test/include/display.fake.h b/deps/fff/examples/weak_linking/test/include/display.fake.h new file mode 100644 index 0000000..ef2adae --- /dev/null +++ b/deps/fff/examples/weak_linking/test/include/display.fake.h @@ -0,0 +1,10 @@ +#ifndef _AUTOFAKE_DISPLAY_H +#define _AUTOFAKE_DISPLAY_H + +#include "fff.h" +#include "display.h" + +DECLARE_FAKE_VALUE_FUNC( bool, display_init ); +DECLARE_FAKE_VOID_FUNC( display_update, const char* ); + +#endif // _AUTOFAKE_DISPLAY_H diff --git a/deps/fff/examples/weak_linking/test/include/error.fake.h b/deps/fff/examples/weak_linking/test/include/error.fake.h new file mode 100644 index 0000000..ee127ec --- /dev/null +++ b/deps/fff/examples/weak_linking/test/include/error.fake.h @@ -0,0 +1,10 @@ +#ifndef _AUTOFAKE_ERROR_H +#define _AUTOFAKE_ERROR_H + +#include "fff.h" +#include "error.h" + +DECLARE_FAKE_VOID_FUNC( runtime_error, const char* ); +DECLARE_FAKE_VALUE_FUNC( char*, runtime_error_nice_print, const char* ); + +#endif // _AUTOFAKE_ERROR_H diff --git a/deps/fff/examples/weak_linking/test/include/sensor.fake.h b/deps/fff/examples/weak_linking/test/include/sensor.fake.h new file mode 100644 index 0000000..ae363f8 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/include/sensor.fake.h @@ -0,0 +1,13 @@ + +#ifndef _AUTOMOCK_SENSOR_H +#define _AUTOMOCK_SENSOR_H + +#include "fff.h" +#include "sensor.h" + +DECLARE_FAKE_VALUE_FUNC( bool, sensor_init ); +DECLARE_FAKE_VALUE_FUNC( float, sensor_read ); + +void TEST_sensor_generate_data( char* buffer, float value ); + +#endif // _AUTOMOCK_SENSOR_H diff --git a/deps/fff/examples/weak_linking/test/include/test_common.h b/deps/fff/examples/weak_linking/test/include/test_common.h new file mode 100644 index 0000000..a282dd1 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/include/test_common.h @@ -0,0 +1,13 @@ +#pragma once + +#include <assert.h> +#include <stdio.h> + +#include "fff.h" +#include "bus.fake.h" +#include "error.fake.h" + + +void init_tests(); + +extern char GLOBAL_TEST_bus_read_ret[32]; diff --git a/deps/fff/examples/weak_linking/test/src/bus.fake.c b/deps/fff/examples/weak_linking/test/src/bus.fake.c new file mode 100644 index 0000000..e3a9f76 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/bus.fake.c @@ -0,0 +1,4 @@ +#include "bus.fake.h" + +DEFINE_FAKE_VALUE_FUNC( bool, bus_read_write, uint8_t, uint8_t, uint8_t*, int, bool ); +DEFINE_FAKE_VALUE_FUNC( bool, bus_write, uint8_t, uint8_t, const uint8_t*, int, bool ); diff --git a/deps/fff/examples/weak_linking/test/src/display.fake.c b/deps/fff/examples/weak_linking/test/src/display.fake.c new file mode 100644 index 0000000..f8dec66 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/display.fake.c @@ -0,0 +1,4 @@ +#include "display.fake.h" + +DEFINE_FAKE_VALUE_FUNC( bool, display_init ); +DEFINE_FAKE_VOID_FUNC( display_update, const char* ); diff --git a/deps/fff/examples/weak_linking/test/src/display.test.c b/deps/fff/examples/weak_linking/test/src/display.test.c new file mode 100644 index 0000000..269ae32 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/display.test.c @@ -0,0 +1,24 @@ +#include "test_common.h" +#include "display.h" + +DEFINE_FFF_GLOBALS; + + +int main(void) +{ + init_tests(); // Resets common and hook errors to asserts. + + GLOBAL_TEST_bus_read_ret[2] = 0x10; + assert( display_init() == true ); + assert( bus_read_write_fake.call_count == 1); + + display_update( "TEST INFO" ); + assert( bus_read_write_fake.call_count == 1 ); + assert( bus_write_fake.call_count == 1 ); + + GLOBAL_TEST_bus_read_ret[2] = 0x00; + assert( display_init() == false ); + + printf("Test " __FILE__ " ok\n"); + return 0; +} diff --git a/deps/fff/examples/weak_linking/test/src/error.fake.c b/deps/fff/examples/weak_linking/test/src/error.fake.c new file mode 100644 index 0000000..0a39478 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/error.fake.c @@ -0,0 +1,4 @@ +#include "error.fake.h" + +DEFINE_FAKE_VOID_FUNC( runtime_error, const char* ); +DEFINE_FAKE_VALUE_FUNC( char*, runtime_error_nice_print, const char* ); diff --git a/deps/fff/examples/weak_linking/test/src/main.test.c b/deps/fff/examples/weak_linking/test/src/main.test.c new file mode 100644 index 0000000..6ac023c --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/main.test.c @@ -0,0 +1,26 @@ + +#include "display.fake.h" +#include "sensor.fake.h" +#include "test_common.h" + +DEFINE_FFF_GLOBALS; + + +int update_main( void ); + +int main(void) +{ + init_tests(); // Resets common and hook errors to asserts. + + sensor_init_fake.return_val = true; + display_init_fake.return_val = true; + + update_main(); + + assert( sensor_init_fake.call_count == 1 ); + assert( display_init_fake.call_count == 1 ); + assert( display_update_fake.call_count == 1 ); + + printf("Test " __FILE__ " ok\n"); + return 0; +} diff --git a/deps/fff/examples/weak_linking/test/src/sensor.fake.c b/deps/fff/examples/weak_linking/test/src/sensor.fake.c new file mode 100644 index 0000000..4fdaa66 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/sensor.fake.c @@ -0,0 +1,11 @@ +#include "sensor.fake.h" + +DEFINE_FAKE_VALUE_FUNC( bool, sensor_init ); +DEFINE_FAKE_VALUE_FUNC( float, sensor_read ); + +void TEST_sensor_generate_data( char* buffer, float value ) +{ + buffer[0] = 0x10; + buffer[1] = (int)(value); + buffer[2] = (value - buffer[1])*255; +} diff --git a/deps/fff/examples/weak_linking/test/src/sensor.test.c b/deps/fff/examples/weak_linking/test/src/sensor.test.c new file mode 100644 index 0000000..8126cb6 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/sensor.test.c @@ -0,0 +1,23 @@ +#include <stdio.h> + +#include "test_common.h" + +#include "sensor.h" +#include "sensor.fake.h" + +DEFINE_FFF_GLOBALS; + + +int main(void) +{ + init_tests(); + + assert( sensor_init() == true ); + + TEST_sensor_generate_data( GLOBAL_TEST_bus_read_ret, 1.5f ); + float value = sensor_read(); + float value_error = value - 1.5f; + assert( value_error < 0.1f && value_error > -0.1f ); + printf("Test " __FILE__ " ok\n"); + return 0; +} diff --git a/deps/fff/examples/weak_linking/test/src/test_common.c b/deps/fff/examples/weak_linking/test/src/test_common.c new file mode 100644 index 0000000..58c3b44 --- /dev/null +++ b/deps/fff/examples/weak_linking/test/src/test_common.c @@ -0,0 +1,33 @@ +#include "test_common.h" +#include <assert.h> +#include <stdio.h> + +char GLOBAL_TEST_bus_read_ret[32]; + + +void spoof_runtime_error( const char* info ) +{ + fprintf(stderr, "Runtime error: %s\n", info ); + assert(0); +} + +bool spoof_bus_read_write( uint8_t dev, uint8_t registry, uint8_t* buffer, int len, bool assume_echo ) +{ + memcpy( buffer, GLOBAL_TEST_bus_read_ret, len ); + fprintf(stderr, "bus spoof %d %d\n", (int)dev, (int)registry ); + return true; +} + +void init_tests() +{ + memset( GLOBAL_TEST_bus_read_ret, 0x00, sizeof(GLOBAL_TEST_bus_read_ret)); + FFF_RESET_HISTORY(); + + RESET_FAKE(bus_read_write); + RESET_FAKE(bus_write); + RESET_FAKE(runtime_error); + + runtime_error_fake.custom_fake = spoof_runtime_error; + bus_read_write_fake.custom_fake = spoof_bus_read_write; + bus_write_fake.return_val = true; +}
\ No newline at end of file |