File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / regressions.py
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:37:58 2012 UTC (12 years, 3 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_9_1p0, v2_9_1, v2_8_0p0, v2_8_0, v2_7_8, HEAD
libxml2

    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>