Jaa


资源分配测试—失败及恢复

    我们知道所有程序都会和各种资源打交道,硬件资源类型如硬盘,系统资源如句柄,因此如何做好资源相关的测试很重要。大家熟知的是测试资源的泄漏,但这里我想更多的从资源分配失败及恢复角度去谈资源分配测试。

    对于资源通常有如下操作:

1.分配资源

   我们熟知的一个典型例子就是 C语言中'malloc()' 系列函数。

2.释放资源

   同样的一个例子就是C语言中'free()'系列函数。

3.资源计数

   比较常见的例子就是性能计数器,比如文件句柄计数器,它能够提供“有多少文件句柄被打开”的信息

    我们编写软件的时候,我们一般都会经常使用这些资源管理类函数来帮助我们,从而得以申请和释放多种类型的资源。当然我们也会编写一些自己的代码来申请和释放资源。一般情况下,这些代码会处于我们的软件架构的相对底层上。我们可能有一个系统,在这个系统里,我们分配资源,使用资源,还可能管理并监控资源,最后当处理完后释放掉这些资源。同时还存在着当代码运行时替我们分配资源的情况。

    由于各种原因,资源的申请和分配并不总是能够成功的。其中一个原因可能是某种特殊的资源被消耗殆尽了。比如在磁盘空间不够时,我们申请使用更多磁盘资源的话就会失败;还比如内存总是是有限的;句柄空间总是那么多。总之软件有可能因为各种原因而出错,但一个主要的原因就是资源的消耗。

    对于服务型软件而言,提供长期稳定的服务是重要的设计目标之一。从而,相对于客户端程序,它需要运行得更加可靠、健壮。为了确保软件的可靠性和健壮性,我们必须确保它在资源可用率发生异常变动的情况下,它还是可以正常运行。如果某一个程序消耗了所有的内存,那么我们可以假定它肯定会出错了;但如果由于系统内存的不足导致内存分配的偶尔失败,那么我们的软件应不应该完全崩溃?还是应该仅仅让当前操作出错并让软件其他部分能够继续正常运行?同样的问题也适用于其他类型的动态分配并被共享的资源,比如磁盘空间,内存,文件,网络连接等。

    致错测试是一个重要的测试场景,可以帮助我们了解当资源使用达到极限的时候会发生什么。简单地说, 这种测试可以用以下伪代码描述:

Pallocatedthing[<use more than enough space here>]

counter n

While(Pallocatedthing[n++]=allocate(<whatever size you like>))

While(n)

free(Pallocatedthing[n--])

    这种测试很简单,就是申请分配资源直到失败,然后释放掉所有分配的资源。这只是一种最简单的测试,如果你确保它可以工作了,那么还有几件事情等着你去做。

    首先,将它放到一个循环里面,然后执行很多次。它是否依然可以正常工作?

    其次,在它运行的时候观察整个系统,看被测试进程是否有内存,句柄或者其他资源的泄漏。这种场景下的一个典型的BUG就是资源分配函数发生错误时没有被正确处理。即使它不会立即导致崩溃,但仍可能会导致内存泄漏或者错误。这种事情我以前也遇到过。

    如果要更深入此类测试场景,则需要理解资源的依赖关系,以及在软件依次消耗掉所有的这些资源情况下其是否正确处理了所有的资源错误。这方面的一个例子就是处理消息的软件。它将在等待处理的消息在磁盘上保存为一个队列。那么磁盘如果满了的话会发生什么?这种场景可能会比较难设置,它看起来类似于:

1. 限制硬盘空间

2. 将消息注入到队列中

3. 停止从队列中取消息

4. 磁盘满了,会发生什么?相应的工作行为是正确的吗?

5. 重新开始从队列中取消息。是否一切工作正常?

   

    接下来的几个步骤类似于我们上面已经做的,那就是在更多的情况下去重复测试它,并观察结果:

    尝试取出所有的消息,直到队列为空。

    建立一个稳定的消息输入流,但时而暂停并再恢复取出消息,反复执行磁盘空间测试用例,看是否在这样的情况下仍然工作正常? 如果出错的话,是否该出错被正确处理?是否有副作用,比如内存泄漏,句柄泄漏或者数据出错?是否队列的生产者和消费者都正确处理了错误用例?

    在你的软件中还会有很多关于这个主题的情况等待你去挖掘,我不准备将所有可能的情况都列在这里。我想你能够自己考虑这些事情。

 

John Daly

高性能计算美国团队测试经理

翻译:周毅, 高性能计算(HPC)中国团队测试开发工程师