用測試驅動開發的思路重構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驅動的所有的功能都不能用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章