Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit e5a5749
Author: James Fraser <[email protected]>
Date:   Sun Feb 3 19:57:31 2019 +1100

    PR #47: Minor review fixes to tests files.

commit e9f11b9
Author: James Fraser <[email protected]>
Date:   Sun Feb 3 19:57:04 2019 +1100

    PR #47: Minor review fixes.

commit 0a7fbec
Author: Pauli Salmenrinne <[email protected]>
Date:   Tue Jan 22 15:11:10 2019 +0200

    Add example for the weak linking

commit 6477373
Author: susundberg <[email protected]>
Date:   Wed Mar 21 13:14:05 2018 +0200

    Add "FFF_FUNCTION_ATTRIBUTES" definition that can be used to declare attributes for functions. More specifically, allow __weak__ attribute.
  • Loading branch information
wulfgarpro committed Feb 3, 2019
1 parent 2c5ecf5 commit 0b9e9f5
Show file tree
Hide file tree
Showing 28 changed files with 575 additions and 165 deletions.
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [How do I fake a function that returns a value by reference?](#how-do-i-fake-a-function-that-returns-a-value-by-reference)
- [How do I fake a function with a function pointer parameter?](#how-do-i-fake-a-function-with-a-function-pointer-parameter)
- [How do I reuse a fake across multiple test-suites?](#how-do-i-reuse-a-fake-across-multiple-test-suites)
- [Specifying GCC Function Attributes](#specifying-gcc-function-attributes)
- [Find out more...](#find-out-more)
- [Benefits](#benefits)
- [Under the hood](#under-the-hood)
Expand Down Expand Up @@ -603,6 +604,26 @@ DEFINE_FAKE_VALUE_FUNC_VARARG(int, value_function_vargs, const char *, int, ...)
DEFINE_FAKE_VOID_FUNC_VARARG(void_function_vargs, const char *, int, ...);

```
## Specifying GCC Function Attributes
You can specify GCC function attributes for your fakes using the `FFF_GCC_FUNCTION_ATTRIBUTES` directive.
### Weak Functions
One usful attribute is the _weak_ attribute that marks a function such that it can be overridden by a non-weak variant at link time. Using weak functions in combination with FFF can help simplify your testing approach.
For example:
* Define a library of fake functions, e.g. libfake.a.
* Link a binary (you might have many) that defines a subset of real variants of the fake functions to the aforementioned fake library.
* This has the benefit of allowing a binary to selectively use a subset of the required fake functions while testing the real variants without the need for many different make targets.
You can mark all fakes with the weak attribute like so:
```
#define FFF_GCC_FUNCTION_ATTRIBUTES __attribute__((weak))
#include "fff.h"
```
See the example project that demonstrates the above approach: _./examples/weak_linking_.
## Find out more...
Expand All @@ -621,8 +642,9 @@ So whats the point?
## Under the Hood
* The fff.h header file is generated by a ruby script
* There are tests under src/test
* There is an example for testing an embedded UI and a hardware driver under src/examples
* There are tests under _./test_
* There is an example for testing an embedded UI and a hardware driver under _./examples_
* There is an example of weak_linking under _./examples_
## Cheat Sheet
Expand Down
4 changes: 4 additions & 0 deletions buildandtest
Original file line number Diff line number Diff line change
Expand Up @@ -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

4 changes: 3 additions & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
@@ -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

39 changes: 39 additions & 0 deletions examples/weak_linking/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
BUILD_DIR = ../../build
INCLUDE_DIRS = -I "../../" -I "./src/"

BUILD_DIR_FAKE = $(BUILD_DIR)/weak_linking
CC = gcc
WEAK_FLAGS=-Wall -DFFF_GCC_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 comes 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 an object from the fake object -> gcc linker will include it.
$(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 where we need weak linking:
# - We want to fake 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)
30 changes: 30 additions & 0 deletions examples/weak_linking/src/bus.c
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 6 additions & 0 deletions examples/weak_linking/src/bus.h
Original file line number Diff line number Diff line change
@@ -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 );
29 changes: 29 additions & 0 deletions examples/weak_linking/src/display.c
Original file line number Diff line number Diff line change
@@ -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!");
}
}
5 changes: 5 additions & 0 deletions examples/weak_linking/src/display.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once
#include "error.h"

bool display_init();
void display_update( const char* info );
10 changes: 10 additions & 0 deletions examples/weak_linking/src/error.c
Original file line number Diff line number Diff line change
@@ -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;
}
7 changes: 7 additions & 0 deletions examples/weak_linking/src/error.h
Original file line number Diff line number Diff line change
@@ -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 );
27 changes: 27 additions & 0 deletions examples/weak_linking/src/main.c
Original file line number Diff line number Diff line change
@@ -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;
}
29 changes: 29 additions & 0 deletions examples/weak_linking/src/sensor.c
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 6 additions & 0 deletions examples/weak_linking/src/sensor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#pragma once

#include "error.h"

bool sensor_init();
float sensor_read();
4 changes: 4 additions & 0 deletions examples/weak_linking/test/bus.fake.c
Original file line number Diff line number Diff line change
@@ -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 );
10 changes: 10 additions & 0 deletions examples/weak_linking/test/bus.fake.h
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions examples/weak_linking/test/display.fake.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include "display.fake.h"

DEFINE_FAKE_VALUE_FUNC( bool, display_init );
DEFINE_FAKE_VOID_FUNC( display_update, const char* );
10 changes: 10 additions & 0 deletions examples/weak_linking/test/display.fake.h
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions examples/weak_linking/test/display.test.c
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 4 additions & 0 deletions examples/weak_linking/test/error.fake.c
Original file line number Diff line number Diff line change
@@ -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* );
10 changes: 10 additions & 0 deletions examples/weak_linking/test/error.fake.h
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions examples/weak_linking/test/main.test.c
Original file line number Diff line number Diff line change
@@ -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;
}
11 changes: 11 additions & 0 deletions examples/weak_linking/test/sensor.fake.c
Original file line number Diff line number Diff line change
@@ -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;
}
13 changes: 13 additions & 0 deletions examples/weak_linking/test/sensor.fake.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 0b9e9f5

Please sign in to comment.