在Linux上伪造IO错误

时间:2014-01-09 23:52:21

标签: python c linux unit-testing

我在Linux上有一个Python和C应用程序,它应该在从磁盘读取文件时正确处理IO错误。应用程序的大部分是用Python编写的,带有C扩展,用于执行IO。在此扩展中,检测到IO错误。

有两种情况可能会出现错误。

  1. 文件丢失。
  2. 使用stat时,文件在磁盘上显示的大小(使用fread)。
  3. 我可以很容易地测试和处理1号案例。但是,我还想为案例2编写单元测试。但是,我不知道如何为测试触发“假的”IO错误。这甚至可能吗?有没有更好的方法来测试这种错误?

4 个答案:

答案 0 :(得分:6)

errno(3)仅设为EIO

   EIO    Input/output error (POSIX.1)

另外,根据read(2)

   EIO    I/O error.  This will happen for example when the process is
          in a background process group, tries to read from its
          controlling terminal, and either it is ignoring or blocking
          SIGTTIN or its process group is orphaned.  It may also occur
          when there is a low-level I/O error while reading from a 
          disk or tape.

并根据write(2)表示:

   EIO    A low-level I/O error occurred while modifying the inode.

因此模拟特定的错误代码可能很困难;请注意,I / O还有其他系统调用,特别是writev(2)和(间接)mmap(2),但read(2)write(2)是最常见的系统调用。

另请注意,file systemsLinux kernel(例如其VFS图层)是caching数据。你可以稍晚或永远得到EIO。请参阅sync(2)fsync(2)

但是,通常情况下,大多数软件都不会特别处理EIO w.r.t.其他错误代码;您可能通过获取其他错误代码进行了足够的测试,例如

  EDQUOT The user's quota of disk blocks on the filesystem containing
          the file referred to by fd has been exhausted.

因此,您可能会通过限制disk quotas(请参阅quotactl(2)setquota(8)等...)和文件空间(参见setrlimit(2) RLIMIT_FSIZE来测试。 },prlimit(1)ulimit内置bash(1)等...)

如果你真的想特意伪造EIO,你可能会对设备造成物理损坏(或者可能只是在错误的时刻拔掉USB磁盘)或者编写自己的Filesystem in User Space(FUSE)来模拟它。我不认为这是值得的(因为当某些东西得到EIO时,整个计算机变得非常快速无法使用,并且用户会注意到这一点......并且因为大多数软件同样处理所有错误代码 - 除了对于EINTR

在代码的C部分中,您可能希望使用strerror(3)(可能使用syslog(3))和/或perror(3)。我不确定以大多数其他错误处理EIO的方式是非常值得的。

注意:许多关键域都有标准,用于定义如何处理错误以及应该开发和测试代码,例如:汽车ISO26262或航空电子设备DO-178B。遵循您所在领域的标准。

答案 1 :(得分:2)

据我所知,TDD的经典警告我们不要为第三方接口(包括标准库)编写模拟/存根,请参阅例如here。主要问题是应用程序代码和通用第三方库之间通常存在差距,这很难与模拟对象联系起来。此外,这会阻止您使用测试来导出设计问题。

(即使在您的情况下,C库并不完全是第三方,单元测试意味着您可以单独测试实体)。

我们的想法是,您编写一个适配器类,它封装所有低级逻辑,并公开一个接近您的应用程序所需的接口(例如,提出更有意义的异常,如FileIsTooBig)。然后根据您的域编写模拟对象。至于测试适配器本身,它只通过几个简单的系统测试进行了测试。

答案 2 :(得分:0)

使用fusepy

fusepy是FUSE顶部的python层,允许在Linux用户空间中实现文件系统。 fusepy是一个Python模块,为FUSE和MacFUSE提供简单的接口。它只是一个文件,使用ctypes实现。使用fusepy,您可以修改public static void fragmentChanged() { Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show(); } 函数实现的行为,并根据需要抛出write。我使用EIO示例作为基础。

答案 3 :(得分:0)

libfiu(如"How can I simulate a failed disk during testing?" answer中所述)是一种结构化的方法,可使用插入方法来执行POSIX调用的故障注入,并且非常适合在测试套件中使用。

list of Linux disk fault injection mechanisms"Special File that causes I/O error" question答案中提到了更一般的技术列表(例如,使用FUSE文件系统)。