用测试驱动开发的思路重构ADC LINUX驱动(一)

用测试驱动开发的思路重构ADC LINUX驱动(一)

前言

测试驱动开发(TDD)是上个世纪末开始流行的一种敏捷开发模式。在大型的互联网应用或者知名IT公司中有不少拥趸,而本人在开发嵌入式代码的时候,从来没有实践或者使用TDD。从本人躺过的无数个坑里面,深感如果单元测试覆盖率高,或者用TDD的方式开发,或许会少很多低级的、逻辑上的、甚至是很多高级的错误。

国外有一本关于嵌入式TDD的教材–James W. Grenning 《Test-Driver Development for Embedded C》,中译本为《测试驱动的嵌入式C语言开发》,由机械工业出版社出版。读了本书后,还是有些失望,里面介绍的一些方法或许更适合在单片机或者ucos这样的微内核里适用,在嵌入式linux驱动开发里,不太合适。

本着试试的心态,用书中的一些方法结合linux的特点来尝试一下,在linux环境下的TDD或许有别样的效果和乐趣。我已经迫不及待的想尝试下。。。

可以参考原生sdk中的驱动:
AST2500片内ADC驱动详解

本文的所有源代码都可以在如下链接上下载:
https://github.com/WangKaiwh/linux_CPP_C_codes/tree/master/linux_driver/ADC_TDD
https://github.com/WangKaiwh/linux_CPP_C_codes/tree/master/linux_driver/test_template

开发环境

编译和运行环境:ubuntu10.04。用的比较老的版本,主要是为了更接近嵌入式的环境,嵌入式所用的linux内核版本一般不高。

定义通用测试框架–Unity的简化版

test_template.h

#ifndef __C_TEST_TEMPLATE_H__
#define __C_TEST_TEMPLATE_H__

#ifdef __cplusplus  
    extern "C"
#endif

#ifndef false 
#define false   0
#endif

#ifndef true
#define true    1 
#endif

#if __KERNEL__
    // ...  ##__VA_ARGS__, windows and linux compatible
    #define OUTPUT_MSG(fmt, ...) do {\
        printk(KERN_EMERG fmt, ##__VA_ARGS__);\
    } while (0)
#else
    // ...  ##__VA_ARGS__, windows and linux compatible
    #define OUTPUT_MSG(fmt, ...) do {\
        printf(fmt, ##__VA_ARGS__); \
    } while (0)
#endif

static inline int __ASSERT_EQUAL_INT(
        const char *file,
        const char *func,
        int line,
        int expected, 
        int actual)
{
    if (expected != actual)
    {
        OUTPUT_MSG("%s, %s, %d, FAILED! expected: %d, actual: %d\n", 
                file, func, line, expected, actual);
        return false;
    }
    OUTPUT_MSG("%s, %s, %d, OK\n", file, func, line);
    return true;
}

#define TEST_ASSERT_EQUAL_INT(expected, actual) do{\
    if (false == __ASSERT_EQUAL_INT(__FILE__, __func__, __LINE__, expected, actual))\
            return -1;\
    } while (0)

#endif

James的书中提到用Unity这个测试框架来进行TDD,在linux里面移植较为麻烦。暂且自己现实了一个简化版本–test_template.h,功能远不及Unity,先凑合着用。
关于Unity工具的使用和下载,参见如下链接:
http://www.throwtheswitch.org/unity

增加和搭建代码框架

新建adc_drv.c,复制sdk中的头文件和Makefile到本地,链接或者复制test_template到本地目录。
修改Makefile,使之可以正常编译。
环境搭建好后,主体代码如下:

#define ENABLE_TDD      1

// product codes


// tdd codes
#if ENABLE_TDD > 0
#include "test_template/test_template.h"

#endif

int __init adc_mod_init(void)
{
    return 0;
}

void __exit adc_mod_cleanup (void)
{

}

MODULE_LICENSE ("GPL");
MODULE_AUTHOR("WangKai -- https://blog.csdn.net/kao2406");
MODULE_DESCRIPTION("ADC driver");

module_init (adc_mod_init);
module_exit (adc_mod_cleanup);

仅仅定义了模块的入口出口。下面开始正式TDD了。

TDD STEP1列功能清单

TDD最重要的一个步骤是列功能清单,站在使用者(API调用者)的角度去思考,产品代码有哪些功能需要完成。只有功能清单足够全面、覆盖率足够高,TDD的效果才会更好。这类似于一个夹具,当夹具足够精细时候,它所能抓住的东西更多,更少的遗漏一些细小的琐碎的BUG。
如下是我列出的本ADC驱动的功能清单:
1 模块加载后,所有的ADC通道0~15,都是默认disable的
2 模块加载后,默认的时钟频率分频0x40
3 模块加载后,注册字符设备
4 模块加载后,提供ioctl给用户态调用。
5 模块加载后,ioctl提供的功能有,设置ADC的时钟,测量ADC,开启ADC的通道
6 正常运行的驱动,用户enable某个通道后,才能正常测试ADC
7 正常运行的驱动,没有enable的某个通道,不能正常测试ADC
8 正常运行的驱动,用户是可以改变任意通道频率的,哪怕是disable的通道
9 正常运行的驱动,已经enable的某个通道,在关闭后,不能正常测试,此功能和7不同,功能9是动态的
10 驱动模块卸载后,字符设备消失
11 驱动模块卸载后,ADC驱动的所有的功能都不能用

阅读更多

没有更多推荐了,返回首页