Numerico test di regressione
-
20-08-2019 - |
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
oEXIT_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.]
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.