unit testing错误条件 – EINTR

简而言之,如何在系统调用上对EINTR等错误条件进行unit testing。

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

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

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

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

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

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

如果你absosmurfly不得不重现这种情况,你可以在Linux中编写自己的驱动程序,在.release方法上返回-EINTR。 看一下O’Reilly的Linux Device Drivers一书中的示例代码 。 sc项目是最简单的项目之一。 你会把它改成这样:

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

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

编辑 – 好吧它看起来像融合 – 用户空间文件系统可能能够。 这用于sshfs之类的东西。 尽管如此仍然是一个优势。

我不认为有一种简单的方法来实际测试这个。

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

祝你好好重现那种情况。

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

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

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

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

这是我如何测试它,只是为了测试,因为fizzer提醒我们调用fclose()两次是不安全的。

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

这是minunit的一体化示例。

 #include  #include  #include  /* 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; } 

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

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

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

阅读[有趣]手册页

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