From 0a7fbeceecd8aa6af859bf2d869fc2e44d16ca85 Mon Sep 17 00:00:00 2001 From: Pauli Salmenrinne Date: Tue, 22 Jan 2019 15:11:10 +0200 Subject: [PATCH] Add example for the weak linking --- buildandtest | 4 ++ examples/Makefile | 4 +- examples/weak_linking/Makefile | 48 +++++++++++++++++++++++ examples/weak_linking/src/bus.c | 32 +++++++++++++++ examples/weak_linking/src/bus.h | 5 +++ examples/weak_linking/src/display.c | 32 +++++++++++++++ examples/weak_linking/src/display.h | 7 ++++ examples/weak_linking/src/error.c | 12 ++++++ examples/weak_linking/src/error.h | 7 ++++ examples/weak_linking/src/main.c | 32 +++++++++++++++ examples/weak_linking/src/sensor.c | 32 +++++++++++++++ examples/weak_linking/src/sensor.h | 6 +++ examples/weak_linking/test/bus.fake.c | 5 +++ examples/weak_linking/test/bus.fake.h | 11 ++++++ examples/weak_linking/test/display.fake.c | 4 ++ examples/weak_linking/test/display.fake.h | 11 ++++++ examples/weak_linking/test/display.test.c | 27 +++++++++++++ examples/weak_linking/test/error.fake.c | 4 ++ examples/weak_linking/test/error.fake.h | 11 ++++++ examples/weak_linking/test/main.test.c | 28 +++++++++++++ examples/weak_linking/test/sensor.fake.c | 12 ++++++ examples/weak_linking/test/sensor.fake.h | 13 ++++++ examples/weak_linking/test/sensor.test.c | 22 +++++++++++ examples/weak_linking/test/test_common.c | 33 ++++++++++++++++ examples/weak_linking/test/test_common.h | 13 ++++++ 25 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 examples/weak_linking/Makefile create mode 100644 examples/weak_linking/src/bus.c create mode 100644 examples/weak_linking/src/bus.h create mode 100644 examples/weak_linking/src/display.c create mode 100644 examples/weak_linking/src/display.h create mode 100644 examples/weak_linking/src/error.c create mode 100644 examples/weak_linking/src/error.h create mode 100644 examples/weak_linking/src/main.c create mode 100644 examples/weak_linking/src/sensor.c create mode 100644 examples/weak_linking/src/sensor.h create mode 100644 examples/weak_linking/test/bus.fake.c create mode 100644 examples/weak_linking/test/bus.fake.h create mode 100644 examples/weak_linking/test/display.fake.c create mode 100644 examples/weak_linking/test/display.fake.h create mode 100644 examples/weak_linking/test/display.test.c create mode 100644 examples/weak_linking/test/error.fake.c create mode 100644 examples/weak_linking/test/error.fake.h create mode 100644 examples/weak_linking/test/main.test.c create mode 100644 examples/weak_linking/test/sensor.fake.c create mode 100644 examples/weak_linking/test/sensor.fake.h create mode 100644 examples/weak_linking/test/sensor.test.c create mode 100644 examples/weak_linking/test/test_common.c create mode 100644 examples/weak_linking/test/test_common.h diff --git a/buildandtest b/buildandtest index 7490cda..bd9945b 100755 --- a/buildandtest +++ b/buildandtest @@ -14,3 +14,7 @@ build/fff_test_glob_c build/fff_test_glob_cpp --gtest_output=xml:build/test_global_results.xml build/driver_testing --gtest_output=xml:build/driver_testing.xml build/driver_testing_fff --gtest_output=xml:build/driver_testing_fff.xml +build/weak_linking/test_display +build/weak_linking/test_sensor +build/weak_linking/test_main + diff --git a/examples/Makefile b/examples/Makefile index f352de3..d1671b6 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,7 +1,9 @@ all: cd embedded_ui; $(MAKE) all cd driver_testing; $(MAKE) all - + cd weak_linking; $(MAKE) all clean: cd embedded_ui; $(MAKE) clean cd driver_testing; $(MAKE) clean + cd weak_linking; $(MAKE) clean + diff --git a/examples/weak_linking/Makefile b/examples/weak_linking/Makefile new file mode 100644 index 0000000..970d147 --- /dev/null +++ b/examples/weak_linking/Makefile @@ -0,0 +1,48 @@ + +BUILD_DIR = ../../build +INCLUDE_DIRS = -I "../../" -I "./src/" + +BUILD_DIR_FAKE = $(BUILD_DIR)/weak_linking +CC = gcc +WEAK_FLAGS=-Wall -DFFF_FUNCTION_ATTRIBUTES="__attribute__((weak))" + +$(BUILD_DIR_FAKE)/%.o: test/%.c + @echo "Compiling "$@ + $(CC) $(WEAK_FLAGS) $(INCLUDE_DIRS) -g -O0 -c $< -o $@ + + + + +FAKE_OBJECTS = $(BUILD_DIR_FAKE)/display.fake.o $(BUILD_DIR_FAKE)/sensor.fake.o $(BUILD_DIR_FAKE)/sensor.fake.o $(BUILD_DIR_FAKE)/error.fake.o $(BUILD_DIR_FAKE)/bus.fake.o $(BUILD_DIR_FAKE)/test_common.o + +TEST_BINARIES = $(BUILD_DIR_FAKE)/test_main $(BUILD_DIR_FAKE)/test_display $(BUILD_DIR_FAKE)/test_sensor +mkdir: + mkdir -p $(BUILD_DIR_FAKE)/ + +clean: + rm -rf $(BUILD_DIR_FAKE)/ + +$(BUILD_DIR_FAKE)/libfakes.a: $(FAKE_OBJECTS) + ar r $@ $^ + +# +# First case where we need __weak__ linking: if we have the build objects (for some reason) in order where the fake +# object compes first +$(BUILD_DIR_FAKE)/test_display: ./test/display.test.c $(BUILD_DIR_FAKE)/libfakes.a ./src/display.c + $(CC) $(WEAK_FLAGS) $(INCLUDE_DIRS) -o $@ $^ + +# +# Second case where we need weak linking: If we use a object from the fake object -> gcc linker will include it +# One can go around also in another way: by making the fake objects in separate files. +# +$(BUILD_DIR_FAKE)/test_sensor: ./test/sensor.test.c ./src/sensor.c $(BUILD_DIR_FAKE)/libfakes.a + $(CC) $(WEAK_FLAGS) $(INCLUDE_DIRS) -o $@ $^ + +# +# Third case: we want to mock one function but not all. +# +$(BUILD_DIR_FAKE)/test_main: ./test/main.test.c ./src/main.c $(BUILD_DIR_FAKE)/libfakes.a + $(CC) $(WEAK_FLAGS) $(INCLUDE_DIRS) -o $@ $^ + + +all: mkdir $(TEST_BINARIES) diff --git a/examples/weak_linking/src/bus.c b/examples/weak_linking/src/bus.c new file mode 100644 index 0000000..f257ac0 --- /dev/null +++ b/examples/weak_linking/src/bus.c @@ -0,0 +1,32 @@ +#pragma once + +uint16_t* BUS_REGISTER_BASE = (uint16_t*)0xFADEFADE; + +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 ) +{ + + // well 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/examples/weak_linking/src/bus.h b/examples/weak_linking/src/bus.h new file mode 100644 index 0000000..af57307 --- /dev/null +++ b/examples/weak_linking/src/bus.h @@ -0,0 +1,5 @@ +#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/examples/weak_linking/src/display.c b/examples/weak_linking/src/display.c new file mode 100644 index 0000000..b3a68c4 --- /dev/null +++ b/examples/weak_linking/src/display.c @@ -0,0 +1,32 @@ +#include + +#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/examples/weak_linking/src/display.h b/examples/weak_linking/src/display.h new file mode 100644 index 0000000..cd10916 --- /dev/null +++ b/examples/weak_linking/src/display.h @@ -0,0 +1,7 @@ +#pragma once +#include "error.h" + +bool display_init(); +void display_update( const char* info ); + + diff --git a/examples/weak_linking/src/error.c b/examples/weak_linking/src/error.c new file mode 100644 index 0000000..fe4d78f --- /dev/null +++ b/examples/weak_linking/src/error.c @@ -0,0 +1,12 @@ +#include +#include "error.h" + +// Function void runtime_error( const char* msg ) is defined on another file. + +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/examples/weak_linking/src/error.h b/examples/weak_linking/src/error.h new file mode 100644 index 0000000..dd52269 --- /dev/null +++ b/examples/weak_linking/src/error.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +void runtime_error( const char* msg ); +char* runtime_error_nice_print( const char* msg ); diff --git a/examples/weak_linking/src/main.c b/examples/weak_linking/src/main.c new file mode 100644 index 0000000..5425fda --- /dev/null +++ b/examples/weak_linking/src/main.c @@ -0,0 +1,32 @@ + +#include "display.h" +#include "sensor.h" +#include +#include + + +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"); + + // Well i guess normally we would have here a for loop. + char buffer[32]; + float reading = sensor_read(); + snprintf( buffer, 32, "Sensor: %0.1f", reading ); + display_update( buffer ); + return 0; + +} + diff --git a/examples/weak_linking/src/sensor.c b/examples/weak_linking/src/sensor.c new file mode 100644 index 0000000..4cdcfc5 --- /dev/null +++ b/examples/weak_linking/src/sensor.c @@ -0,0 +1,32 @@ + +#include + +#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/examples/weak_linking/src/sensor.h b/examples/weak_linking/src/sensor.h new file mode 100644 index 0000000..cd68f2e --- /dev/null +++ b/examples/weak_linking/src/sensor.h @@ -0,0 +1,6 @@ +#pragma once +#include "error.h" + +bool sensor_init(); +float sensor_read(); + diff --git a/examples/weak_linking/test/bus.fake.c b/examples/weak_linking/test/bus.fake.c new file mode 100644 index 0000000..1316051 --- /dev/null +++ b/examples/weak_linking/test/bus.fake.c @@ -0,0 +1,5 @@ + + +#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/examples/weak_linking/test/bus.fake.h b/examples/weak_linking/test/bus.fake.h new file mode 100644 index 0000000..12b292b --- /dev/null +++ b/examples/weak_linking/test/bus.fake.h @@ -0,0 +1,11 @@ + + +#ifndef _AUTOMOCK_BUS_H +#define _AUTOMOCK_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 // _AUTOMOCK_BUS_H diff --git a/examples/weak_linking/test/display.fake.c b/examples/weak_linking/test/display.fake.c new file mode 100644 index 0000000..ac38152 --- /dev/null +++ b/examples/weak_linking/test/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/examples/weak_linking/test/display.fake.h b/examples/weak_linking/test/display.fake.h new file mode 100644 index 0000000..9f9ea57 --- /dev/null +++ b/examples/weak_linking/test/display.fake.h @@ -0,0 +1,11 @@ + +#ifndef _AUTOMOCK_DISPLAY_H +#define _AUTOMOCK_DISPLAY_H + +#include "fff.h" +#include "display.h" + +DECLARE_FAKE_VALUE_FUNC( bool, display_init ); +DECLARE_FAKE_VOID_FUNC( display_update, const char* ); + +#endif // _AUTOMOCK_DISPLAY_H diff --git a/examples/weak_linking/test/display.test.c b/examples/weak_linking/test/display.test.c new file mode 100644 index 0000000..dff16fe --- /dev/null +++ b/examples/weak_linking/test/display.test.c @@ -0,0 +1,27 @@ + +#include "test_common.h" + +#include "display.h" + +DEFINE_FFF_GLOBALS; + + +int main(void) +{ + init_tests(); // will reset 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/examples/weak_linking/test/error.fake.c b/examples/weak_linking/test/error.fake.c new file mode 100644 index 0000000..54febcc --- /dev/null +++ b/examples/weak_linking/test/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/examples/weak_linking/test/error.fake.h b/examples/weak_linking/test/error.fake.h new file mode 100644 index 0000000..1edbf4a --- /dev/null +++ b/examples/weak_linking/test/error.fake.h @@ -0,0 +1,11 @@ + +#ifndef _AUTOMOCK_ERROR_H +#define _AUTOMOCK_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 // _AUTOMOCK_ERROR_H diff --git a/examples/weak_linking/test/main.test.c b/examples/weak_linking/test/main.test.c new file mode 100644 index 0000000..2b07e35 --- /dev/null +++ b/examples/weak_linking/test/main.test.c @@ -0,0 +1,28 @@ + +#include "display.fake.h" +#include "sensor.fake.h" +#include "test_common.h" + +DEFINE_FFF_GLOBALS; + +int update_main( void ); + +int main(void) +{ + + init_tests(); // will reset 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/examples/weak_linking/test/sensor.fake.c b/examples/weak_linking/test/sensor.fake.c new file mode 100644 index 0000000..428af28 --- /dev/null +++ b/examples/weak_linking/test/sensor.fake.c @@ -0,0 +1,12 @@ + + +#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/examples/weak_linking/test/sensor.fake.h b/examples/weak_linking/test/sensor.fake.h new file mode 100644 index 0000000..ae363f8 --- /dev/null +++ b/examples/weak_linking/test/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/examples/weak_linking/test/sensor.test.c b/examples/weak_linking/test/sensor.test.c new file mode 100644 index 0000000..40f261b --- /dev/null +++ b/examples/weak_linking/test/sensor.test.c @@ -0,0 +1,22 @@ +#include +#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/examples/weak_linking/test/test_common.c b/examples/weak_linking/test/test_common.c new file mode 100644 index 0000000..6f35a72 --- /dev/null +++ b/examples/weak_linking/test/test_common.c @@ -0,0 +1,33 @@ +#include "test_common.h" +#include +#include + +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; +} diff --git a/examples/weak_linking/test/test_common.h b/examples/weak_linking/test/test_common.h new file mode 100644 index 0000000..a282dd1 --- /dev/null +++ b/examples/weak_linking/test/test_common.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +#include "fff.h" +#include "bus.fake.h" +#include "error.fake.h" + + +void init_tests(); + +extern char GLOBAL_TEST_bus_read_ret[32];