搜尋
首頁資料庫mysql教程Accessing static Data and Functions in Legacy C

http://www.renaissancesoftware.net/blog/archives/450 It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped f

http://www.renaissancesoftware.net/blog/archives/450

It’s a new year; last year was a leap year; so the quadrennial reports of leap year bugs are coming in. Apologies are in the press from Apple, TomTom, and Microsoft. Trains we stopped from running in China. Somehow calling them glitches seems to make it someone else’s fault, something out of their control. How long have leap years been around? Julius Caesar introduced Leap Years in the Roman empire over 2000 years ago. The Gregorian calendar has been around since 1682. This is not a new idea, or a new bug.

I’m going to try to take one excuse away from the programmers that create these bugs by answering a question that comes up all the time, “How do I test static functions in my C code?”

In code developed using TDD, static functions are tested indirectly through the public interface. As I mentioned in a this article TDD is a code rot radar. It helps you see design problems. Needing direct access to hidden functions and data in C is a sign of code rot. It is time to refactor.

But what about existing legacy code that has statics? It is probably way past the time for idealism and time for some pragmatism. In this article and the next, we’ll look at how to get your code untouched into the test harness and access those pesky <span>static </span>variables and functions.

If you don’t mind touching your code, you could change all mentions of static to STATIC. Then using the preprocessor, STATIC can he set to static during production and to nothing for test, making the names globally accessible. In gcc you would use these command line options

  • For production builds use -dSTATIC=static
  • For unit test builds use -dSTATIC

Let’s look at two options that, at least for access to statics, you will not have to touch your code to get it under test. First is <span>#include</span>-ing your .c in the test file. In the next article we’ll build a test adapter .c that give access to the hidden parts.

We are going to revisit code that is similar to the original code responsible for the Zune Bug. I rewrote the code to avoid attracting any lawyers but it is structured similarly to the original Zune driver, complete with static data and functions that must be correct for the function to work.

The header file provides a data structure and initialization function for figuring out the information about the date. Here is the header:

typedef struct Date
{
    int daysSince1980;
    int year;
    int dayOfYear;
    int month;
    int dayOfMonth;
    int dayOfWeek;
} Date;
 
void Date_Init(Date *, int daysSince1980);
 
enum {
    SUN = 0, MON, TUE, WED, THU, FRI, SAT
};
 
enum {
    JAN = 0, FEB, MAR, APR, MAY, JUN,
    JUL, AUG, SEP, OCT, NOV, DEC
};

Date_Init populates the Date instance passed in. Ignoring the fact that I can probably fully test this by rigging the daysSince1980, and inspecting the contents of the resultingDate structure, we are going to see how we can directly test the hidden functions and data.

Date_Init has three hidden helper functions.

void Date_Init(Date* date, int daysSince1980)
{
     date->daysSince1980 = daysSince1980;
     FirstSetYearAndDayOfYear(date);
     ThenSetMonthAndDayOfMonth(date);
     FinallySetDayOfWeek(date);
}

Date_Init is the tip of the iceberg. All the interesting stuff is happening in the hidden data and functions:

#include "Date.h"
#include 
 
enum
{
    STARTING_YEAR = 1980, STARTING_WEEKDAY = TUE
};
 
static const int nonLeapYearDaysPerMonth[12] =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
static const int leapYearDaysPerMonth[12] =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
static bool IsLeapYear(int year)
{
    if (year % 400 == 0)
        return true;
    if (year % 100 == 0)
        return false;
    if (year % 4 == 0)
        return true;
    return false;
}
 
static int GetDaysInYear(int year)
{
    if (IsLeapYear(year))
        return 366;
    else
        return 365;
}
 
static void FirstSetYearAndDayOfYear(Date * date)
{
    int days = date->daysSince1980;
    int year = STARTING_YEAR;
    int daysInYear = GetDaysInYear(year);
 
    while (days > daysInYear)
    {
        year++;
        days -= daysInYear;
        daysInYear = GetDaysInYear(year);
    }
 
    date->dayOfYear = days;
    date->year = year;
}
 
static void ThenSetMonthAndDayOfMonth(Date * date)
{
    int month = 0;
    int days = date->dayOfYear;
    const int * daysPerMonth = nonLeapYearDaysPerMonth;
    if (IsLeapYear(date->year))
        daysPerMonth = leapYearDaysPerMonth;
 
    while (days > daysPerMonth[month])
    {
        days -= daysPerMonth[month];
        month++;
    }
    date->month = month + 1;
    date->dayOfMonth = days;
}
 
static void FinallySetDayOfWeek(Date * date)
{
     date->dayOfWeek =((date->daysSince1980-1)+STARTING_WEEKDAY)%7;
}
 
void Date_Init(Date* date, int daysSince1980)
{
     date->daysSince1980 = daysSince1980;
     FirstSetYearAndDayOfYear(date);
     ThenSetMonthAndDayOfMonth(date);
     FinallySetDayOfWeek(date);
}

Let’s say you want to check the days per month vectors. You might want to write a test to check the months against the handy poem we use here in the US: Thirty days, has September, April, June and November; all the rest have thirty-one, except for February. It has 28 except in leap year it has 29.

If you started by writing this test…

TEST(Date, sept_has_30_days)
{
    LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]);
}

… you get this error:

DateTest.cpp:41: error: 'nonLeapYearDaysPerMonth' was not declared in this scope

Let’s get access to the hidden statics in the test case by including Date.c instead ofDate.h in DateTest.cpp. The full test case file looks like this now:

#include "CppUTest/TestHarness.h"
 
extern "C"
{
#include "Date.c"
}
 
TEST_GROUP(Date)
{
};
 
TEST(Date, sept_has_30_days)
{
    LONGS_EQUAL(30, nonLeapYearDaysPerMonth[SEP]);
}

With a little refactoring days per month vectors can be checked like this:

#include "CppUTest/TestHarness.h"
 
extern "C"
{
#include "Date.c"
}
 
TEST_GROUP(Date)
{
    const int * daysPerYearVector;
 
    void setup()
    {
        daysPerYearVector = nonLeapYearDaysPerMonth;
    }
 
    void itsLeapYear()
    {
        daysPerYearVector = leapYearDaysPerMonth;
    }
 
    void CheckNumberOfDaysPerMonth(int month, int days)
    {
        LONGS_EQUAL(days, daysPerYearVector[month]);
    }
 
    void ThirtyDaysHasSeptEtc()
    {
        CheckNumberOfDaysPerMonth(SEP, 30);
        CheckNumberOfDaysPerMonth(APR, 30);
        CheckNumberOfDaysPerMonth(JUN, 30);
        CheckNumberOfDaysPerMonth(NOV, 30);
 
        CheckNumberOfDaysPerMonth(OCT, 31);
        CheckNumberOfDaysPerMonth(DEC, 31);
        CheckNumberOfDaysPerMonth(JAN, 31);
        CheckNumberOfDaysPerMonth(MAR, 31);
        CheckNumberOfDaysPerMonth(MAY, 31);
        CheckNumberOfDaysPerMonth(JUL, 31);
        CheckNumberOfDaysPerMonth(AUG, 31);
    }
 
    void ExceptFebruaryHas(int days)
    {
      CheckNumberOfDaysPerMonth(FEB, days);
    }
};
 
TEST(Date, non_leap_year_day_per_month_table)
{
    ThirtyDaysHasSeptEtc();
    ExceptFebruaryHas(28);
}
 
TEST(Date, leap_year_day_per_month_table)
{
    itsLeapYear();
    ThirtyDaysHasSeptEtc();
    ExceptFebruaryHas(28);
}

You have access to all the hidden stuff, so you can write the test for the static functions:

IsLeapYear()GetDaysInYear()FirstSetYearAndDayOfYear(),ThenSetMonthAndDayOfMonth(), and FinallySetDayOfWeek().

If Date been an abstract data type, with its data structure hidden in the .c file, the tests would all have access to structure members hidden from the rest of the world.

There is a downside to this approach, which probably does not matter in this case, but could in others. You can only have one test file that can include a given .c file. In the next article we’ll solve that problem.

Have you heard of any interesting leap year bugs? Did you prevent your own?

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
data文件夹里面是什么数据data文件夹里面是什么数据May 05, 2023 pm 04:30 PM

data文件夹里面是系统及程序的数据,比如软件的设置和安装包等,Data文件夹中各个文件夹则代表的是不同类型的数据存放文件夹,无论Data文件指的是文件名Data还是扩展名data,都是系统或程序自定义的数据文件,Data是数据保存的备份类文件,一般可以用meidaplayer、记事本或word打开。

Java中的static、this、super、final怎么使用Java中的static、this、super、final怎么使用Apr 18, 2023 pm 03:40 PM

一、static  请先看下面这段程序:publicclassHello{publicstaticvoidmain(String[]args){//(1)System.out.println("Hello,world!");//(2)}}看过这段程序,对于大多数学过Java的从来说,都不陌生。即使没有学过Java,而学过其它的高级语言,例如C,那你也应该能看懂这段代码的意思。它只是简单的输出“Hello,world”,一点别的用处都没有,然而,它却展示了static关键字的主

c语言static的作用和用法是什么c语言static的作用和用法是什么Jan 31, 2024 pm 01:59 PM

c语言static的作用和用法:1、变量作用域;2、生命周期;3、函数内部;4、修饰全局变量;5、修饰函数;6、其他用途;详细介绍:1、变量作用域,当一个变量前有static关键字,那么这个变量的作用域被限制在声明它的文件内,也就是说,这个变量是“文件级作用域”,这对于防止变量的“重复定义”问题很有用;2、生命周期,静态变量在程序开始执行时初始化一次,并在程序结束时销毁等等。

static的作用static的作用Jan 24, 2024 pm 04:08 PM

static的作用:1、变量;2、方法;3、类;4、其他用途;5、多线程环境;6、性能优化;7、单例模式;8、常量;9、局部变量;10、内存布局优化;11、避免重复初始化;12、在函数中使用。详细介绍:1、变量,静态变量,当一个变量被声明为static时,它属于类级别,而不是实例级别,这意味着无论创建多少个对象,都只有一个静态变量存在,所有对象都共享这个静态变量等等。

mysql load data乱码怎么办mysql load data乱码怎么办Feb 16, 2023 am 10:37 AM

mysql load data乱码的解决办法:1、找到出现乱码的SQL语句;2、修改语句为“LOAD DATA LOCAL INFILE "employee.txt" INTO TABLE EMPLOYEE character set utf8;”即可。

C语言中static关键字的实际应用场景及使用技巧C语言中static关键字的实际应用场景及使用技巧Feb 21, 2024 pm 07:21 PM

C语言中static关键字的实际应用场景及使用技巧一、概述static是C语言中的一个关键字,用于修饰变量和函数。它的作用是改变其在程序运行过程中的生命周期和可见性,使得变量和函数具有静态的特性。本文将介绍static关键字的实际应用场景及使用技巧,并通过具体的代码示例进行说明。二、静态变量延长变量的生命周期使用static关键字修饰局部变量可以将其生命周期

SQL语句中的AND运算符和OR运算符怎么用SQL语句中的AND运算符和OR运算符怎么用May 28, 2023 pm 04:34 PM

SQLAND&OR运算符AND和OR运算符用于基于一个以上的条件对记录进行过滤。AND和OR可在WHERE子语句中把两个或多个条件结合起来。如果第一个条件和第二个条件都成立,则AND运算符显示一条记录。如果第一个条件和第二个条件中只要有一个成立,则OR运算符显示一条记录。"Persons"表:LastNameFirstNameAddressCityAdamsJohnOxfordStreetLondonBushGeorgeFifthAvenueNewYorkCarter

xdata和data有哪些区别xdata和data有哪些区别Dec 11, 2023 am 11:30 AM

区别有:1、xdata通常指的是自变量,data则是指整个数据集;2、xdata主要用于建立数据分析模型,data则是用于进行数据分析和统计;3、xdata通常用于回归分析、方差分析、预测建模,data则可以使用各种统计方法进行分析;4、xdata通常需要进行数据预处理,data则可以包含完整的原始数据。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版