#!/usr/bin/env python import sys import os import time import tempfile import pickle import string from MiscFunctions import * from geniconfig import GenIPrecompiled, GenIBasic, GenIBatch, globalStatistics from reports import Report from shutil import copytree from optparse import OptionParser from gtesterrc import * import signal, os def intHandler(signum, frame): print 'Aborting geni-test-run.py', signum sys.exit(1) class NullTestParams: def configure(self, testId, suiteName, extractedFile): self.dirStructure = TestDirectoryStructure() self.testId = testId.replace(' ', '_') self.extractedFile = extractedFile self.suiteName = suiteName self.batches = range(1, 2, 1) self.globalStats = {} def setVariants(self, baseline, variants): self.baseline = baseline self.variants = variants def assertConsistency(self): # Test the consistency of the parameters return 0 def aboutThisTest(self): return [ ("test id ", self.testId), ("extracted from", self.extractedFile) ] def printOut(self): print('Test parameters:\n') for kv in self.aboutThisTest(): print ' - %s\t\t: %s' % kv def save(self, path): f = open(path, 'w') pickle.dump(self, f); f.close() def load(self, path): f = open(path, 'r') conf = pickle.load(f) f.close() return conf def generateStatistics(params): chdirOrDie(params.dirStructure.resultsDir) report = Report(params) report.generateFullReport() if report.canCompile(): print "Compiling report..." report.compile() class TestDirectoryStructure: def __init__(self): self.initialDir = os.getcwd() self.binDir = self.initialDir + '/bin' self.scratchDir = None def setScratchDir(self, newScratchDir): self.scratchDir = newScratchDir self.testsDir = self.scratchDir + '/tests' self.responsesDir = self.scratchDir + '/responses' self.resultsDir = self.scratchDir + '/results' def nameForBatch(self, batch): return 'batch-%s' % str(batch) def testDirForBatch(self, batch): return '%s/%s' % (self.testsDir, self.nameForBatch(batch)) def responsesDirForTestable(self, variant): return '%s/%s' % (self.responsesDir, variant.id()) def batchResponseDirForTestable(self, variant, batch): return '%s/%s' % (self.responsesDirForTestable(variant), self.nameForBatch(batch)) ###################### # M A I N ###################### # Set the signal handler for Ctrl-C. We have code which keeps the gtester # running in case geni dies on one of the test cases, but this code also # makes it hard to Ctrl-C out of gtester. Having our own signal handler # gives us back this ability. signal.signal(signal.SIGINT, intHandler) runOnly = False parser = OptionParser() parser.add_option("-i", "--id", dest="testId", default="test", help="run with test id ID", metavar="ID") parser.add_option("", "--baseline", dest="runOnlyPath", help="reuse baseline data from DIR", metavar="DIR") parser.add_option("-m", "--macros", dest="macros", help="use FILE as the GenI macros", metavar="FILE") parser.add_option("-l", "--lexicon", dest="lexicon", help="use FILE as the GenI lexicon", metavar="FILE") parser.add_option("-s", "--suite", dest="suite", help="use FILE as the GenI suite (batch only)", metavar="FILE") parser.add_option("-x", "--extracted", dest="extracted", help="DIR in which the testsuite was extracted", metavar="DIR") parser.add_option("", "--suitename", dest="suiteName", default="unknown", help="use NAME in the report as the name of the test suite", metavar="NAME") parser.add_option("", "--variants", dest="variantsStr", default="sim-2 sim-2-p", help="space-delimited list of GenI variants to run", metavar="LIST") parser.add_option("-o", "--output", dest="output", help="Save results in DIR (autogenerated path by default)", metavar="DIR") parser.add_option("-r", "--report", dest="reportOnly", action="store_true", help="Regenerate report only. Read data from output dir") parser.add_option("", "--genibatch", dest="genibatch", action="store_true", help="Use batch testing GenI instead of regular GenI") parser.add_option("", "--geniprecomp", dest="geniprecomp", action="store_true", help="Use with precompiled-grammar instead of regular GenI") parser.add_option("", "--nobaseline", dest="baseline", default=True, action="store_false", help="Do not run baseline variant") parser.add_option("", "--geniflags", dest="geniflags", help="Extra flags to pass to GenI (usually not needed)") (options, args) = parser.parse_args() def tryhelp(s): return s + " Try --help" if not options.reportOnly: if options.geniprecomp and options.genibatch: parser.error(tryhelp("Options --genibatch and --geniprecomp are mutually exclusive")) if options.geniprecomp and options.macros: parser.error(tryhelp("Options --macros and --geniprecomp are mutually exclusive.")) if not options.geniprecomp and not options.macros: parser.error(tryhelp("Option --macros is mandatory unless --geniprecomp is present.")) if options.genibatch and not options.suite: parser.error(tryhelp("Option --suite is mandatory if --genibatch is present.")) if not options.genibatch and options.extracted: parser.error(tryhelp("Option --extracted is mandatory unless --genibatch is present.")) if not options.genibatch and options.suite: parser.error(tryhelp("Option --suite is forbidden unless --genibatch is present.")) # ---------------------------------------------------------------------- # set up scratch directory # ---------------------------------------------------------------------- parseOnly = options.reportOnly __params = NullTestParams() if not options.reportOnly: # read in command line args runOnly = options.runOnlyPath argVariants = string.split(options.variantsStr) __params.configure(options.testId, options.suiteName, options.extracted) def geniVariant(name, xtra): if options.geniflags: xtra = xtra + [options.geniflags] if options.geniprecomp: return GenIPrecompiled(options.lexicon, name, __params, extraArgs=xtra) elif options.genibatch: return GenIBatch(options.macros, options.lexicon, options.suite, name, __params, extraArgs=xtra) else: return GenIBasic(options.macros, options.lexicon, name, __params, extraArgs=xtra) # read the requested variants from the command line variants = [] for r in argVariants: for v in KNOWN_CONFIGS(): if v[0] == r: variants.append(geniVariant(v[0], v[1])) # add also a baseline variant for some statistical collection if options.baseline: baseline = geniVariant('null', ["-b null", "--metrics='%s'" % ' '.join(BASELINE_METRICS)]) else: baseline = False __params.setVariants(baseline, variants) timestr = time.strftime("%Y-%m-%dT%H%M") if options.output: scratchDir = options.output else: scratchDir = tempfile.mkdtemp(prefix='scratch-%s-%s-%s.' % (__params.testId, options.suiteName, timestr), dir=__params.dirStructure.initialDir) scratchDirLatest = 'scratch-%s-latest' % __params.testId scratchDirSubLatest = 'scratch-%s-%s-latest' % (__params.testId, options.suiteName) __params.dirStructure.setScratchDir(scratchDir) __params.save(__params.dirStructure.scratchDir + '/testConfiguration.pickle') if os.access(scratchDirLatest, os.F_OK): os.unlink(scratchDirLatest) os.symlink(scratchDir, scratchDirLatest) if os.access(scratchDirSubLatest, os.F_OK): os.unlink(scratchDirSubLatest) os.symlink(scratchDir, scratchDirSubLatest) else: __params = __params.load('%s/testConfiguration.pickle' % options.output) # ---------------------------------------------------------------------- # run tests # ---------------------------------------------------------------------- # Test file structure if not parseOnly: #assertExecExists(execGenI) mkdirsOrDie(__params.dirStructure.testsDir) mkdirsOrDie(__params.dirStructure.responsesDir) else: assertFileExists(__params.dirStructure.testsDir) assertFileExists(__params.dirStructure.responsesDir) if not os.access(__params.dirStructure.resultsDir, os.F_OK): mkdirsOrDie(__params.dirStructure.resultsDir) __params.printOut() __params.assertConsistency() for variant in __params.variants: for f in variant.requiredBinaries(): assertExecExists(f) for f in variant.requiredBinaries(): assertFileExists(f) for (batch, clauses) in enumerate(__params.batches): batch += 1 if parseOnly: actionStr = "Parsing" else: actionStr = "Running" print '%s batch %i / %i...' % (actionStr, batch, len(__params.batches)) batchDir = __params.dirStructure.testDirForBatch(batch) # copy in the extracted cases if not parseOnly: copytree(options.extracted, batchDir) else: assertFileExists(batchDir) # collect information to organise the statistics for this batch __params.globalStats[batch] = globalStatistics(batchDir) # run the variants, and collect stats baselineVariant = __params.baseline variantsToRun = __params.variants if baselineVariant: variantsToRun = [baselineVariant] + variantsToRun for variant in variantsToRun: # make a directory for the responses of the variant, and change to it batchResponseDir = __params.dirStructure.batchResponseDirForTestable(variant,batch) print " - %s" % variant.id() # if it's in runonly mode, we copy the baseline data from some path if variant == baselineVariant and runOnly: print "copying from %s" % options.runOnlyPath mkdirsOrDie(os.path.dirname(batchResponseDir)) copytree(options.runOnlyPath, batchResponseDir) elif not parseOnly: mkdirsOrDie(batchResponseDir) variant.runOnBatch(batchDir, batchResponseDir) # collect the statistical data for variant in variantsToRun: batchResponseDir = __params.dirStructure.batchResponseDirForTestable(variant,batch) variant.parseBatchOutputFiles(batch, batchDir, batchResponseDir) # ---------------------------------------------------------------------- # generate report # ---------------------------------------------------------------------- chdirOrDie(__params.dirStructure.resultsDir) generateStatistics(__params)