C代码的打桩框架 - FFF (Fake Function Framework)

我只是IT圈的搬运工,项目原址https://github.com/meekrosoft/fff

FFF是一个用宏实现的小框架,只需要一个头文件fff.h,没有任何其他依赖,非常简洁。

FFF的核心就是三个宏:

FAKE_VOID_FUNC(fn [,arg_types*]);

定义一个名为fn的桩函数,返回值为空,有n个参数。

FAKE_VALUE_FUNC(return_type, fn [,arg_types*]);

定义一个名为fn的桩函数,返回值类型为return_type,有n个参数。

RESET_FAKE(fn);

重置桩函数fn的调用信息。

实现上,FFF为桩生成了一系列变量。以带有返回值并且有一个参数的桩函数为例:

#define DECLARE_FAKE_VALUE_FUNC1(RETURN_TYPE, FUNCNAME, ARG0_TYPE)     EXTERN_C         typedef struct FUNCNAME##_Fake {             DECLARE_ARG(ARG0_TYPE, 0, FUNCNAME)             DECLARE_ALL_FUNC_COMMON             DECLARE_VALUE_FUNCTION_VARIABLES(RETURN_TYPE)             RETURN_TYPE(*custom_fake)(ARG0_TYPE arg0);         } FUNCNAME##_Fake;        extern FUNCNAME##_Fake FUNCNAME##_fake;        void FUNCNAME##_reset();     END_EXTERN_C \

DECLARE_ARG生成了存储当前参数值的变量和存储历史参数值的数组。

#define DECLARE_ARG(type, n, FUNCNAME)     type arg##n##_val;     type arg##n##_history[FFF_ARG_HISTORY_LEN];

DECLARE_ALL_FUNC_COMMON定义了桩被调用的次数,参数历史数组的长度以及调用HISTORY_DROPPED的次数。

#define DECLARE_ALL_FUNC_COMMON     unsigned int call_count;     unsigned int arg_history_len;    unsigned int arg_histories_dropped; \

DECLARE_VALUE_FUNCTION_VARIABLES定义了桩函数返回值相关的数据。

custom_fake提供了一个回调接口,可以用自己的函数来构造返回值。

实例:

#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;
}
时间: 2024-10-10 14:40:22

C代码的打桩框架 - FFF (Fake Function Framework)的相关文章

Mosquitto pub/sub服务实现代码浅析-主体框架

Mosquitto 是一个IBM 开源pub/sub订阅发布协议 MQTT 的一个单机版实现(目前也只有单机版),MQTT主打轻便,比较适用于移动设备等上面,花费流量少,解析代价低.相对于XMPP等来说,简单许多. MQTT采用二进制协议,而不是XMPP的XML协议,所以一般消息甚至只需要花费2个字节的大小就可以交换信息了,对于移动开发比较有优势. IBM虽然开源了其MQTT消息协议,但是却没有开源其RSMB服务端程序,不过还好目前有比较稳定的实现可用,本文的Mosquitto是其中比较活跃的实

使用Swift代码演示Cocoa框架

通过使用简单的代码学习Cocoa框架,每一个例子都通过代码和StoryBoard实现,并且总结他们的各自特点 所有完整代码将会托管到github库,https://github.com/land-pack/xxx-xxx.git 使用TabBarController(代码实现) 使用TabBarController(StoryBoard实现)

9行代码体现集合框架中的一个小细节

String[] strs = {"string--01", "string--02", "string--03", "string--04"};List<String> strsList = Arrays.asList(strs);//以下语句输出:[string--01, string--02, string--03, string--04]System.out.println(strsList); int[]

Android软硬整合设计与框架揭秘: HAL&amp;Framework &amp;Native Service &amp;App&amp;HTML5架构设计与实战开发

掌握Android从底层开发到框架整合技术到上层App开发及HTML5的全部技术: 一次彻底的Android架构.思想和实战技术的洗礼: 彻底掌握Andorid HAL.Android Runtime.Android Framework.Android Native Service.Android Binder.Android App.Android Testing.HTML5技术的源泉和精髓等核心技术,不仅仅是技术和代码本身,更重要的是背后的设计思想和商业哲学. 一.课程特色 l  贯通And

基于.NET的微软ORM框架视频教程(Entity Framework技术)

基于.NET的微软ORM框架视频教程(Entity Framework技术) 第一讲  ORM映射 第二讲 初识EntifyFramework框架 第三讲 LINQ表达式查询 第四讲 LINQ方法查询 第五讲 LINQ TO Entities 第六讲 ObjectQuery查询(上) 第七讲 ObjectQuery查询(下) 第八讲 Entity中的增删改及事务处理 第九讲 Entity中的存储过程使用(完) 源代码及视频

代码 | 用ALNS框架求解一个TSP问题 - 代码详解

写在前面 前面好多篇文章,我们总算是把整个ALNS的代码框架给大家说明白了.不知道大家对整个框架了解了没有.不过打铁要趁热,心急了要吃热豆腐.今天就来实战一下,教大家怎么用ALNS的代码框架,求解一个老生常谈的TSP问题,so,get ready? 01 文件说明 整个项目由多个文件组成,为了大家更好了解各个文件的内容以及他们之间的关系,小编特地做了一份表格说明. 类名或文件名 说明 main 主文件 TSPSolution Solution的定义和各种相关操作 TSP_LS LocalSear

框架页面尽可以这么用(后置代码中控制框架)

下面是框架页: <%@ Page CodeBehind="Frameset.aspx.cs" Language="c#" AutoEventWireup="false" Inherits="IbatisTest.Web.Frameset" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"><HTML>

H5常用代码:页面框架

万变不离其宗,道法自然! 虽然H5的小项目一波又一波,但有一个东东基本没什么变化,那就是整个页面的框架结构. 我所常用的H5常用页面框架如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no&q

利用AOP实现空模式的无缝使用 创世纪代码应用之一:一行代码让接口框架RUN起来

这是我开播第一篇,朋友们多多支持.捧场,谢谢. 引子 地是空虚混沌.渊面黑暗. 神的灵运行在水面上.  神说.要有光.就有了光.  神看光是好的.就把光暗分开了.  神称光为昼.称暗为夜.有晚上.有早晨.这是头一日. ——引至<圣经.神创造天地> 关键词:null,AOP,Spring.Net框架,空模式,面向接口编程,单元测试,方法拦截器 摘要:在我们编程的时候很难离开null,它给我们带来了很多麻烦.本文从新的视角利用AOP无缝使用空模式部分解决了这个问题,最重要的是可以使得我们的程序尽早