Domanda

Sto lavorando su un calcolo scientifico codice (scritto in C++), e oltre all'esecuzione di unit test per i componenti più piccoli, vorrei fare il test di regressione su alcuni metodi numerici per l'uscita dal confronto di una "valida" la risposta delle precedenti revisioni.Ci sono alcune caratteristiche che mi piacerebbe:

  • Consentire il confronto tra i numeri di una tolleranza specificata (sia per errore di arrotondamento, e looser aspettative)
  • La capacità di distinguere tra int, doppie, ecc, e di ignorare il testo, se necessario
  • Ben output formattato per raccontare cosa è andato storto e dove:in un multi-colonna della tabella di dati, mostra solo la voce di colonna che si differenzia
  • Ritorno EXIT_SUCCESS o EXIT_FAILURE a seconda che il file che corrispondono

Ci sono buoni script o applicazioni che fanno questo, o devo roll mia in Python per leggere e confrontare i file di output?Sicuramente non sono la prima persona con questo tipo di esigenze.

[Quello che segue non è strettamente pertinenti, ma può fattore nella decisione di cosa fare.Io uso CMake e embedded CTest funzionalità delle unità di test che utilizzano il Test di Google framework.Immagino che non dovrebbe essere difficile aggiungere un paio di add_custom_command dichiarazioni nel mio CMakeLists.txt chiamare qualunque sia la regressione software di cui ho bisogno.]

È stato utile?

Soluzione 3

Ho finito per scrivere uno script Python per fare più o meno quello che volevo.

#!/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)

Altri suggerimenti

Si dovrebbe andare per PyUnit, che ora fa parte dello standard lib sotto il nome di unittest.Supporta tutto ciò che hai chiesto.Il controllo della tolleranza, ad esempio, è fatto con assertAlmostEqual().

Il ndiff utilità potrebbe essere vicino a quello che si sta cercando:è come confronto, ma il confronto di file di testo di numeri a una tolleranza desiderata.

So di essere molto in ritardo alla festa, ma un paio di mesi fa avevo scritto il nrtest utilità nel tentativo di rendere questo flusso di lavoro più facile.Suona come potrebbe aiutare troppo.

Ecco una rapida panoramica.Ogni test è definito dal suo file di input e il relativo file di output.A seguito di esecuzione, i file di output vengono memorizzati in un portatile benchmark directory.Una seconda fase, poi confronta il benchmark di riferimento (benchmark.Un recente aggiornamento ha abilitato le estensioni utente, così si può definire funzioni di confronto per i tuoi dati personalizzati.

Spero che aiuta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top