单元测试错误条件 - EINTR

时间:2008-10-06 22:47:10

标签: c unit-testing errno eintr

简而言之,如何在系统调用中对EINTR等错误情况进行单元测试。

我正在研究的一个特殊例子,它本身就是一个案例,就是当它返回带有(errno == EINTR)的EOF时是否有必要再次调用fclose。行为取决于fclose的实现:

// Given an open FILE *fp
while (fclose(fp)==EOF && errno==EINTR) {
    errno = 0;
}

如果在发生EINTR时释放fp,则此调用可能不安全。如何测试何时(errno == EINTR)的错误处理?

8 个答案:

答案 0 :(得分:4)

在这种特殊情况下,再次调用fclose()是不安全的,因为C标准表示即使调用失败,流也会从文件中解除关联(并变得不确定)。

答案 1 :(得分:3)

使用函数指针结构来抽象出系统函数。将调用替换为flcose(fp),类似于sys-> fclose(fp)。在单元测试中,创建一个fclose实现,它始终返回EINTR,然后将sys-> fclose设置为该版本。

答案 2 :(得分:3)

查看linux内核源代码,我无法找到任何在文件关闭时返回EINTR的驱动程序。

如果你absosmurfly必须重现这种情况,你可以在Linux中编写自己的驱动程序,在.release方法上返回-EINTR。看看O'Reilly的Linux设备驱动程序书中的example code。 sc项目是最简单的项目之一。你会把它改成这样:

int scull_release(struct inode *inode, struct file *filp)
{
    return -EINTR;
}

再说一次,通过浏览linux源代码树,我找不到任何会在关闭时返回EINTR的驱动程序。

编辑 - 好吧它看起来像保险丝 - 用户空间文件系统可能能够。这用于sshfs之类的东西。但仍然是一个优势。

答案 3 :(得分:2)

我认为没有一种简单的方法可以随意进行实际测试。

如果fclose操作被信号中断,将生成EINTR。 这意味着fclose在后台线程中,它在处理关闭请求时接收到信号。

祝你好运,重现那种情况。

答案 4 :(得分:1)

正如Kris建议的那样,暂时用您自己在单元测试代码中定义的实现来替换对“真实”fclose()的调用。您的实现可以做一些简单的事情:

int my_fclose(FILE *fp)
{
  errno = EINTR;
  return EOF;
}

但是,正如fizzer指出的那样,你不应该再次调用fclose(),因为行为是未定义的,所以你甚至不必费心去检查条件。

要问的另一个问题是你是否真的需要担心这个问题;如果您的应用程序代码在fclose()期间阻止了所有可能的信号(SIGKILL和SIGSTOP除外),那么您将无法获得EINTR,也不需要担心。

答案 5 :(得分:1)

以下是我测试它的方法,仅仅是为了测试,因为fizzer提醒我们两次调用fclose()是不安全的。

您可以使用自己的行为 重新定义 fclose()(或libc的任何其他功能)。在类Unix系统上,链接器不会抱怨 - 从未尝试在Windows上使用cygwin。当然,这会阻止您的其他测试使用真正的fclose(),因此必须将此类测试放在单独的测试可执行文件中。

以下是minunit的一体化示例。

#include <errno.h>
#include <stdio.h>
#include <stdbool.h>

/* from minunit.h : http://www.jera.com/techinfo/jtns/jtn002.html */
 #define mu_assert(message, test) do { if (!(test)) return message; } while (0)
 #define mu_run_test(test) do { char *message = test(); tests_run++; \
                                if (message) return message; } while (0)

int tests_run = 0;
bool fclose_shall_fail_on_EINTR = false;

//--- our implemention of fclose()
int fclose(FILE *fp) {
    if (fclose_shall_fail_on_EINTR) {
        errno = EINTR;
        fclose_shall_fail_on_EINTR = false;   //--- reset for next call 
        return EOF;
    } else { return 0; }
}

//--- this is the "production" function to be tested 
void uninterruptible_close(FILE *fp) {
    // Given an open FILE *fp
     while (fclose(fp)==EOF && errno==EINTR) {
        errno = 0;
     }
}

char *test_non_interrupted_fclose(void) {
    FILE *theHandle = NULL;   //--- don't care here
    uninterruptible_close(theHandle);
    mu_assert("test fclose wo/ interruption", 0 == errno);
    return 0;
}

char *test_interrupted_fclose(void) {
    FILE *theHandle = NULL;   //--- don't care here
    fclose_shall_fail_on_EINTR = true;

    uninterruptible_close(theHandle);
    mu_assert("test fclose wo/ interruption", 0 == errno);
    return 0;
}

char *test_suite(void)
{
    mu_run_test(test_non_interrupted_fclose);
    mu_run_test(test_interrupted_fclose);
    return 0;
}

int main(int ac, char **av)
{
  char *result = test_suite();

  printf("number of tests run: %d\n", tests_run);
  if (result) { printf("FAIL: %s\n", result); } 

  return 0 != result;
}

答案 6 :(得分:1)

嗯,我想你们所有人都忽略了一些非常重要的东西。

fclose()无法返回EINTR。 fopen(),fwrite(),fread()或任何标准C I / O函数都不能。

只有当您涉及低级I / O调用时,例如open(2),write(2)和select(2)才需要处理EINTR。

阅读[有趣]手册页

答案 7 :(得分:1)

我认为同时处理信号和确认错误情况可能存在问题。