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>