2009-06-28 86 views
3

我正在一个科学计算代码(用C++编写),并且除了对较小分量执行单元测试,我想通过比较上做一些数值输出的回归测试来自以前修订版的“已知良好”答案。有几个特点,我想:数值回归测试

  • 允许数字比较,以在指定的误差(对于舍入误差和宽松的预期)
  • 整数的能力来区分,双打等,而如果忽略文本必要
  • 好格式的输出,告诉了什么问题以及哪里:在数据的多列的表格,只显示不同
  • 返回EXIT_SUCCESSEXIT_FAILURE根据文件是否匹配
栏项目

有没有这样做的好脚本或应用程序,或者我将不得不在我们自己的Python中阅读和比较输出文件?当然,我不是第一个有这种要求的人。

[下面是不是严格相关,但它可能因素到什么就做什么的决定。我使用CMake及其嵌入的CTest功能来驱动使用Google Test框架的单元测试。我想,它不应该是很难在我CMakeLists.txt增加一些add_custom_command语句调用任何回归的软件,我需要。]

+0

你所描述的大部分是单元测试的标准功能。你是否要求超出单元测试的东西? – 2009-06-28 20:13:27

+0

根据我的理解,单元测试用先验的答案来看最小的测试。 (如果我编写一个返回值为2的函数returnTwo,则可以进行单元测试以检查返回值是否正确。)回归测试正如我所说的那样,它是生成更高级别的数据并比较未来版本生成的数据与旧数据一起生成。 – 2009-06-29 03:09:58

回答

0

我最终编写了一个Python脚本来做更多或更少的事情。

#!/usr/bin/env python 

import sys 
import re 
from optparse import OptionParser 
from math import fabs 

splitPattern = re.compile(r',|\s+|;') 

class FailObject(object): 
    def __init__(self, options): 
     self.options = options 
     self.failure = False 

    def fail(self, brief, full = ""): 
     print ">>>> ", brief 
     if options.verbose and full != "": 
      print "  ", full 
     self.failure = True 


    def exit(self): 
     if (self.failure): 
      print "FAILURE" 
      sys.exit(1) 
     else: 
      print "SUCCESS" 
      sys.exit(0) 

def numSplit(line): 
    list = splitPattern.split(line) 
    if list[-1] == "": 
     del list[-1] 

    numList = [float(a) for a in list] 
    return numList 

def softEquiv(ref, target, tolerance): 
    if (fabs(target - ref) <= fabs(ref) * tolerance): 
     return True 

    #if the reference number is zero, allow tolerance 
    if (ref == 0.0): 
     return (fabs(target) <= tolerance) 

    #if reference is non-zero and it failed the first test 
    return False 

def compareStrings(f, options, expLine, actLine, lineNum): 
    ### check that they're a bunch of numbers 
    try: 
     exp = numSplit(expLine) 
     act = numSplit(actLine) 
    except ValueError, e: 
#  print "It looks like line %d is made of strings (exp=%s, act=%s)." \ 
#    % (lineNum, expLine, actLine) 
     if (expLine != actLine and options.checkText): 
      f.fail("Text did not match in line %d" % lineNum) 
     return 

    ### check the ranges 
    if len(exp) != len(act): 
     f.fail("Wrong number of columns in line %d" % lineNum) 
     return 

    ### soft equiv on each value 
    for col in range(0, len(exp)): 
     expVal = exp[col] 
     actVal = act[col] 
     if not softEquiv(expVal, actVal, options.tol): 
      f.fail("Non-equivalence in line %d, column %d" 
        % (lineNum, col)) 
    return 

def run(expectedFileName, actualFileName, options): 
    # message reporter 
    f = FailObject(options) 

    expected = open(expectedFileName) 
    actual = open(actualFileName) 
    lineNum = 0 

    while True: 
     lineNum += 1 
     expLine = expected.readline().rstrip() 
     actLine = actual.readline().rstrip() 

     ## check that the files haven't ended, 
     # or that they ended at the same time 
     if expLine == "": 
      if actLine != "": 
       f.fail("Tested file ended too late.") 
      break 
     if actLine == "": 
      f.fail("Tested file ended too early.") 
      break 

     compareStrings(f, options, expLine, actLine, lineNum) 

     #print "%3d: %s|%s" % (lineNum, expLine[0:10], actLine[0:10]) 

    f.exit() 

################################################################################ 
if __name__ == '__main__': 
    parser = OptionParser(usage = "%prog [options] ExpectedFile NewFile") 
    parser.add_option("-q", "--quiet", 
         action="store_false", dest="verbose", default=True, 
         help="Don't print status messages to stdout") 

    parser.add_option("--check-text", 
         action="store_true", dest="checkText", default=False, 
         help="Verify that lines of text match exactly") 

    parser.add_option("-t", "--tolerance", 
         action="store", type="float", dest="tol", default=1.e-15, 
         help="Relative error when comparing doubles") 

    (options, args) = parser.parse_args() 

    if len(args) != 2: 
     print "Usage: numdiff.py EXPECTED ACTUAL" 
     sys.exit(1) 

    run(args[0], args[1], options) 
3

你应该去PyUnit,也就是现在的标准库的一部分名称unittest下。它支持你要求的一切。容差检查,例如,用assertAlmostEqual()完成。

0

ndiff工具可以接近你在找什么:它就像差异,但它将数字的文本文件进行比较,以所需的公差。

0

我知道我很迟到了,但几个月前,我在试图使这个工作流程更容易写的nrtest效用。这听起来也可能对你有所帮助。

下面是一个快速概述。每个测试都由其输入文件及其预期输出文件定义。执行后,输出文件存储在便携式基准目录中。然后第二步将此基准与参考基准进行比较。最近的更新已启用用户扩展,因此您可以为自定义数据定义比较函数。

我希望它有帮助。