1
0
mirror of https://github.com/meekrosoft/fff synced 2026-01-23 00:15:59 +01:00

Add example for the weak linking

This commit is contained in:
Pauli Salmenrinne
2019-01-22 15:11:10 +02:00
parent 647737304d
commit 0a7fbeceec
25 changed files with 414 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -0,0 +1,32 @@
#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!");
}
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include "error.h"
bool display_init();
void display_update( const char* info );

View File

@@ -0,0 +1,12 @@
#include <assert.h>
#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;
}

View File

@@ -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 );

View File

@@ -0,0 +1,32 @@
#include "display.h"
#include "sensor.h"
#include <stdlib.h>
#include <stdio.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");
// 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;
}

View File

@@ -0,0 +1,32 @@
#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;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "error.h"
bool sensor_init();
float sensor_read();

View File

@@ -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 );

View File

@@ -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

View File

@@ -0,0 +1,4 @@
#include "display.fake.h"
DEFINE_FAKE_VALUE_FUNC( bool, display_init );
DEFINE_FAKE_VOID_FUNC( display_update, const char* );

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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* );

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,22 @@
#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;
}

View File

@@ -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;
}

View File

@@ -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];