Annotation of embedaddon/libxml2/regressions.py, revision 1.1
1.1 ! misho 1: #!/usr/bin/python -u
! 2: import glob, os, string, sys, thread, time
! 3: # import difflib
! 4: import libxml2
! 5:
! 6: ###
! 7: #
! 8: # This is a "Work in Progress" attempt at a python script to run the
! 9: # various regression tests. The rationale for this is that it should be
! 10: # possible to run this on most major platforms, including those (such as
! 11: # Windows) which don't support gnu Make.
! 12: #
! 13: # The script is driven by a parameter file which defines the various tests
! 14: # to be run, together with the unique settings for each of these tests. A
! 15: # script for Linux is included (regressions.xml), with comments indicating
! 16: # the significance of the various parameters. To run the tests under Windows,
! 17: # edit regressions.xml and remove the comment around the default parameter
! 18: # "<execpath>" (i.e. make it point to the location of the binary executables).
! 19: #
! 20: # Note that this current version requires the Python bindings for libxml2 to
! 21: # have been previously installed and accessible
! 22: #
! 23: # See Copyright for the status of this software.
! 24: # William Brack (wbrack@mmm.com.hk)
! 25: #
! 26: ###
! 27: defaultParams = {} # will be used as a dictionary to hold the parsed params
! 28:
! 29: # This routine is used for comparing the expected stdout / stdin with the results.
! 30: # The expected data has already been read in; the result is a file descriptor.
! 31: # Within the two sets of data, lines may begin with a path string. If so, the
! 32: # code "relativises" it by removing the path component. The first argument is a
! 33: # list already read in by a separate thread; the second is a file descriptor.
! 34: # The two 'base' arguments are to let me "relativise" the results files, allowing
! 35: # the script to be run from any directory.
! 36: def compFiles(res, expected, base1, base2):
! 37: l1 = len(base1)
! 38: exp = expected.readlines()
! 39: expected.close()
! 40: # the "relativisation" is done here
! 41: for i in range(len(res)):
! 42: j = string.find(res[i],base1)
! 43: if (j == 0) or ((j == 2) and (res[i][0:2] == './')):
! 44: col = string.find(res[i],':')
! 45: if col > 0:
! 46: start = string.rfind(res[i][:col], '/')
! 47: if start > 0:
! 48: res[i] = res[i][start+1:]
! 49:
! 50: for i in range(len(exp)):
! 51: j = string.find(exp[i],base2)
! 52: if (j == 0) or ((j == 2) and (exp[i][0:2] == './')):
! 53: col = string.find(exp[i],':')
! 54: if col > 0:
! 55: start = string.rfind(exp[i][:col], '/')
! 56: if start > 0:
! 57: exp[i] = exp[i][start+1:]
! 58:
! 59: ret = 0
! 60: # ideally we would like to use difflib functions here to do a
! 61: # nice comparison of the two sets. Unfortunately, during testing
! 62: # (using python 2.3.3 and 2.3.4) the following code went into
! 63: # a dead loop under windows. I'll pursue this later.
! 64: # diff = difflib.ndiff(res, exp)
! 65: # diff = list(diff)
! 66: # for line in diff:
! 67: # if line[:2] != ' ':
! 68: # print string.strip(line)
! 69: # ret = -1
! 70:
! 71: # the following simple compare is fine for when the two data sets
! 72: # (actual result vs. expected result) are equal, which should be true for
! 73: # us. Unfortunately, if the test fails it's not nice at all.
! 74: rl = len(res)
! 75: el = len(exp)
! 76: if el != rl:
! 77: print 'Length of expected is %d, result is %d' % (el, rl)
! 78: ret = -1
! 79: for i in range(min(el, rl)):
! 80: if string.strip(res[i]) != string.strip(exp[i]):
! 81: print '+:%s-:%s' % (res[i], exp[i])
! 82: ret = -1
! 83: if el > rl:
! 84: for i in range(rl, el):
! 85: print '-:%s' % exp[i]
! 86: ret = -1
! 87: elif rl > el:
! 88: for i in range (el, rl):
! 89: print '+:%s' % res[i]
! 90: ret = -1
! 91: return ret
! 92:
! 93: # Separate threads to handle stdout and stderr are created to run this function
! 94: def readPfile(file, list, flag):
! 95: data = file.readlines() # no call by reference, so I cheat
! 96: for l in data:
! 97: list.append(l)
! 98: file.close()
! 99: flag.append('ok')
! 100:
! 101: # This routine runs the test program (e.g. xmllint)
! 102: def runOneTest(testDescription, filename, inbase, errbase):
! 103: if 'execpath' in testDescription:
! 104: dir = testDescription['execpath'] + '/'
! 105: else:
! 106: dir = ''
! 107: cmd = os.path.abspath(dir + testDescription['testprog'])
! 108: if 'flag' in testDescription:
! 109: for f in string.split(testDescription['flag']):
! 110: cmd += ' ' + f
! 111: if 'stdin' not in testDescription:
! 112: cmd += ' ' + inbase + filename
! 113: if 'extarg' in testDescription:
! 114: cmd += ' ' + testDescription['extarg']
! 115:
! 116: noResult = 0
! 117: expout = None
! 118: if 'resext' in testDescription:
! 119: if testDescription['resext'] == 'None':
! 120: noResult = 1
! 121: else:
! 122: ext = '.' + testDescription['resext']
! 123: else:
! 124: ext = ''
! 125: if not noResult:
! 126: try:
! 127: fname = errbase + filename + ext
! 128: expout = open(fname, 'rt')
! 129: except:
! 130: print "Can't open result file %s - bypassing test" % fname
! 131: return
! 132:
! 133: noErrors = 0
! 134: if 'reserrext' in testDescription:
! 135: if testDescription['reserrext'] == 'None':
! 136: noErrors = 1
! 137: else:
! 138: if len(testDescription['reserrext'])>0:
! 139: ext = '.' + testDescription['reserrext']
! 140: else:
! 141: ext = ''
! 142: else:
! 143: ext = ''
! 144: if not noErrors:
! 145: try:
! 146: fname = errbase + filename + ext
! 147: experr = open(fname, 'rt')
! 148: except:
! 149: experr = None
! 150: else:
! 151: experr = None
! 152:
! 153: pin, pout, perr = os.popen3(cmd)
! 154: if 'stdin' in testDescription:
! 155: infile = open(inbase + filename, 'rt')
! 156: pin.writelines(infile.readlines())
! 157: infile.close()
! 158: pin.close()
! 159:
! 160: # popen is great fun, but can lead to the old "deadly embrace", because
! 161: # synchronizing the writing (by the task being run) of stdout and stderr
! 162: # with respect to the reading (by this task) is basically impossible. I
! 163: # tried several ways to cheat, but the only way I have found which works
! 164: # is to do a *very* elementary multi-threading approach. We can only hope
! 165: # that Python threads are implemented on the target system (it's okay for
! 166: # Linux and Windows)
! 167:
! 168: th1Flag = [] # flags to show when threads finish
! 169: th2Flag = []
! 170: outfile = [] # lists to contain the pipe data
! 171: errfile = []
! 172: th1 = thread.start_new_thread(readPfile, (pout, outfile, th1Flag))
! 173: th2 = thread.start_new_thread(readPfile, (perr, errfile, th2Flag))
! 174: while (len(th1Flag)==0) or (len(th2Flag)==0):
! 175: time.sleep(0.001)
! 176: if not noResult:
! 177: ret = compFiles(outfile, expout, inbase, 'test/')
! 178: if ret != 0:
! 179: print 'trouble with %s' % cmd
! 180: else:
! 181: if len(outfile) != 0:
! 182: for l in outfile:
! 183: print l
! 184: print 'trouble with %s' % cmd
! 185: if experr != None:
! 186: ret = compFiles(errfile, experr, inbase, 'test/')
! 187: if ret != 0:
! 188: print 'trouble with %s' % cmd
! 189: else:
! 190: if not noErrors:
! 191: if len(errfile) != 0:
! 192: for l in errfile:
! 193: print l
! 194: print 'trouble with %s' % cmd
! 195:
! 196: if 'stdin' not in testDescription:
! 197: pin.close()
! 198:
! 199: # This routine is called by the parameter decoding routine whenever the end of a
! 200: # 'test' section is encountered. Depending upon file globbing, a large number of
! 201: # individual tests may be run.
! 202: def runTest(description):
! 203: testDescription = defaultParams.copy() # set defaults
! 204: testDescription.update(description) # override with current ent
! 205: if 'testname' in testDescription:
! 206: print "## %s" % testDescription['testname']
! 207: if not 'file' in testDescription:
! 208: print "No file specified - can't run this test!"
! 209: return
! 210: # Set up the source and results directory paths from the decoded params
! 211: dir = ''
! 212: if 'srcdir' in testDescription:
! 213: dir += testDescription['srcdir'] + '/'
! 214: if 'srcsub' in testDescription:
! 215: dir += testDescription['srcsub'] + '/'
! 216:
! 217: rdir = ''
! 218: if 'resdir' in testDescription:
! 219: rdir += testDescription['resdir'] + '/'
! 220: if 'ressub' in testDescription:
! 221: rdir += testDescription['ressub'] + '/'
! 222:
! 223: testFiles = glob.glob(os.path.abspath(dir + testDescription['file']))
! 224: if testFiles == []:
! 225: print "No files result from '%s'" % testDescription['file']
! 226: return
! 227:
! 228: # Some test programs just don't work (yet). For now we exclude them.
! 229: count = 0
! 230: excl = []
! 231: if 'exclfile' in testDescription:
! 232: for f in string.split(testDescription['exclfile']):
! 233: glb = glob.glob(dir + f)
! 234: for g in glb:
! 235: excl.append(os.path.abspath(g))
! 236:
! 237: # Run the specified test program
! 238: for f in testFiles:
! 239: if not os.path.isdir(f):
! 240: if f not in excl:
! 241: count = count + 1
! 242: runOneTest(testDescription, os.path.basename(f), dir, rdir)
! 243:
! 244: #
! 245: # The following classes are used with the xmlreader interface to interpret the
! 246: # parameter file. Once a test section has been identified, runTest is called
! 247: # with a dictionary containing the parsed results of the interpretation.
! 248: #
! 249:
! 250: class testDefaults:
! 251: curText = '' # accumulates text content of parameter
! 252:
! 253: def addToDict(self, key):
! 254: txt = string.strip(self.curText)
! 255: # if txt == '':
! 256: # return
! 257: if key not in defaultParams:
! 258: defaultParams[key] = txt
! 259: else:
! 260: defaultParams[key] += ' ' + txt
! 261:
! 262: def processNode(self, reader, curClass):
! 263: if reader.Depth() == 2:
! 264: if reader.NodeType() == 1:
! 265: self.curText = '' # clear the working variable
! 266: elif reader.NodeType() == 15:
! 267: if (reader.Name() != '#text') and (reader.Name() != '#comment'):
! 268: self.addToDict(reader.Name())
! 269: elif reader.Depth() == 3:
! 270: if reader.Name() == '#text':
! 271: self.curText += reader.Value()
! 272:
! 273: elif reader.NodeType() == 15: # end of element
! 274: print "Defaults have been set to:"
! 275: for k in defaultParams.keys():
! 276: print " %s : '%s'" % (k, defaultParams[k])
! 277: curClass = rootClass()
! 278: return curClass
! 279:
! 280:
! 281: class testClass:
! 282: def __init__(self):
! 283: self.testParams = {} # start with an empty set of params
! 284: self.curText = '' # and empty text
! 285:
! 286: def addToDict(self, key):
! 287: data = string.strip(self.curText)
! 288: if key not in self.testParams:
! 289: self.testParams[key] = data
! 290: else:
! 291: if self.testParams[key] != '':
! 292: data = ' ' + data
! 293: self.testParams[key] += data
! 294:
! 295: def processNode(self, reader, curClass):
! 296: if reader.Depth() == 2:
! 297: if reader.NodeType() == 1:
! 298: self.curText = '' # clear the working variable
! 299: if reader.Name() not in self.testParams:
! 300: self.testParams[reader.Name()] = ''
! 301: elif reader.NodeType() == 15:
! 302: if (reader.Name() != '#text') and (reader.Name() != '#comment'):
! 303: self.addToDict(reader.Name())
! 304: elif reader.Depth() == 3:
! 305: if reader.Name() == '#text':
! 306: self.curText += reader.Value()
! 307:
! 308: elif reader.NodeType() == 15: # end of element
! 309: runTest(self.testParams)
! 310: curClass = rootClass()
! 311: return curClass
! 312:
! 313:
! 314: class rootClass:
! 315: def processNode(self, reader, curClass):
! 316: if reader.Depth() == 0:
! 317: return curClass
! 318: if reader.Depth() != 1:
! 319: print "Unexpected junk: Level %d, type %d, name %s" % (
! 320: reader.Depth(), reader.NodeType(), reader.Name())
! 321: return curClass
! 322: if reader.Name() == 'test':
! 323: curClass = testClass()
! 324: curClass.testParams = {}
! 325: elif reader.Name() == 'defaults':
! 326: curClass = testDefaults()
! 327: return curClass
! 328:
! 329: def streamFile(filename):
! 330: try:
! 331: reader = libxml2.newTextReaderFilename(filename)
! 332: except:
! 333: print "unable to open %s" % (filename)
! 334: return
! 335:
! 336: curClass = rootClass()
! 337: ret = reader.Read()
! 338: while ret == 1:
! 339: curClass = curClass.processNode(reader, curClass)
! 340: ret = reader.Read()
! 341:
! 342: if ret != 0:
! 343: print "%s : failed to parse" % (filename)
! 344:
! 345: # OK, we're finished with all the routines. Now for the main program:-
! 346: if len(sys.argv) != 2:
! 347: print "Usage: maketest {filename}"
! 348: sys.exit(-1)
! 349:
! 350: streamFile(sys.argv[1])
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>