Changeset 535 for pkpgcounter

Show
Ignore:
Timestamp:
12/08/07 14:09:51 (14 years ago)
Author:
jerome
Message:

Improved testsuite generator.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pkpgcounter/trunk/tests/gstests.py

    r534 r535  
    2929import md5 
    3030import tempfile 
     31import time 
    3132 
    3233MEGABYTE = 1024 * 1024 
    3334 
    34 def getAvailableDevices() : 
    35     """Returns a list of available GhostScript devices. 
    36      
    37        The list is returned without any x11, bbox, nor ijs related device. 
    38     """ 
    39     answerfd = os.popen('/bin/echo "devicenames ==" | gs -dBATCH -dQUIET -dNOPAUSE -dPARANOIDSAFER -sDEVICE=nullpage -', "r") 
    40     answer = answerfd.readline().strip() 
    41     if not answerfd.close() : 
    42         if answer.startswith("[/") and answer.endswith("]") : 
    43             devices = [ dev[1:] for dev in answer[1:-1].split() \ 
    44                                     if dev.startswith("/") \ 
    45                                        and (not dev.startswith("/x11")) \ 
    46                                        and (not dev == "/ijs") \ 
    47                                        and (not dev == "/nullpage") \ 
    48                                        and (not dev == "/bbox") ] 
    49             devices.sort()                            
    50             return devices 
    51     return [] 
    52          
    53 def getAvailableIJSPrintClasses() : 
    54     """Returns a list of available IJS Print Classes. 
    55      
    56        Currently the list is a static one and doesn't contain all the available print classes. 
    57     """ 
    58     return [ "DJ3600", "DJ3320", "DJ9xx", "DJGenericVIP", "LJColor",  
    59              "DJ850", "DJ890", "DJ9xxVIP", "DJ8xx", "DJ540", "DJ660", 
    60              "DJ6xx", "DJ350", "DJ6xxPhoto", "DJ630", "DJ8x5", "DJ4100", 
    61              "AP21xx", "AP2560", "AP2xxx", "PSP100", "PSP470", "Undefined", 
    62              "Postscript", "LJJetReady", "LJMono", "LJFastRaster", 
    63              "LJZjsMono", ] 
    64          
    65 def batchGeneration(infilename, devices, root, command) : 
    66     """Loops over a set of devices calling a particular command.""" 
    67     for device in devices : 
    68         outfilename = "%(root)s.%(device)s" % locals() 
    69         if os.path.exists(outfilename) and os.stat(outfilename).st_size : 
    70             sys.stdout.write("Skipping %(outfilename)s : already exists.\n" % locals()) 
     35class TestSuite : 
     36    """A class for the testsuite.""" 
     37    def __init__(self, inputfile) : 
     38        """Initializes the testsuite.""" 
     39        self.tmp = None 
     40        self.inputfile = inputfile 
     41        self.results = {} 
     42        self.supportedpct = self.failedpct = self.unsupportedpct = None 
     43        self.md5sum = self.computeChecksum() 
     44        self.mastersize = None 
     45         
     46    def __del__(self) :     
     47        """Remove temporary file, if any.""" 
     48        if self.tmp is not None : 
     49            self.tmp.close() 
     50             
     51    def computeChecksum(self) :     
     52        """Computes an MD5 checksum for the input file's content.""" 
     53        checksum = md5.new() 
     54        istemp = False 
     55        if self.inputfile == "-" : 
     56            # Input is standard input, so we must use a temporary 
     57            # file to be able to loop over all available devices. 
     58            self.tmp = tempfile.NamedTemporaryFile(mode="w+b") 
     59            self.inputfile = self.tmp.name 
     60            infile = sys.stdin 
     61            istemp = True 
    7162        else :     
    72             sys.stdout.write("Generating %(outfilename)s " % locals()) 
     63            infile = open(self.inputfile, "rb") 
     64             
     65        while True : 
     66            data = infile.read(MEGABYTE) 
     67            if not data : 
     68                break 
     69            if istemp :     
     70                self.tmp.write(data) 
     71            checksum.update(data) 
     72             
     73        if istemp :     
     74            self.tmp.flush()     
     75        else : 
     76            infile.close() 
     77             
     78        return checksum.hexdigest()     
     79         
     80    def getAvailableDevices(self) : 
     81        """Returns a list of available GhostScript devices. 
     82         
     83           The list is returned without any x11, bbox, nor ijs related device. 
     84        """ 
     85        answerfd = os.popen('/bin/echo "devicenames ==" | gs -dBATCH -dQUIET -dNOPAUSE -dPARANOIDSAFER -sDEVICE=nullpage -', "r") 
     86        answer = answerfd.readline().strip() 
     87        if not answerfd.close() : 
     88            if answer.startswith("[/") and answer.endswith("]") : 
     89                devices = [ dev[1:] for dev in answer[1:-1].split() \ 
     90                                        if dev.startswith("/") \ 
     91                                           and (not dev.startswith("/x11")) \ 
     92                                           and (not dev == "/ijs") \ 
     93                                           and (not dev == "/nullpage") \ 
     94                                           and (not dev == "/bbox") ] 
     95                devices.sort()                            
     96                return devices 
     97        return [] 
     98             
     99    def getAvailableIJSPrintClasses(self) : 
     100        """Returns a list of available IJS Print Classes. 
     101         
     102           Currently the list is a static one and doesn't contain all the available print classes. 
     103        """ 
     104        return [ "DJ3600", "DJ3320", "DJ9xx", "DJGenericVIP", "LJColor",  
     105                 "DJ850", "DJ890", "DJ9xxVIP", "DJ8xx", "DJ540", "DJ660", 
     106                 "DJ6xx", "DJ350", "DJ6xxPhoto", "DJ630", "DJ8x5", "DJ4100", 
     107                 "AP21xx", "AP2560", "AP2xxx", "PSP100", "PSP470", "Undefined", 
     108                 "Postscript", "LJJetReady", "LJMono", "LJFastRaster", 
     109                 "LJZjsMono", ] 
     110             
     111    def batchGeneration(self, infilename, devices, root, command) : 
     112        """Loops over a set of devices calling a particular command.""" 
     113        parts = root.split(".") 
     114        if (len(parts) > 1) and (parts[-1] == "hpijs") : 
     115            devprefix = parts[-1] + "/" 
     116        else : 
     117            devprefix = "" 
     118        for device in devices : 
     119            outfilename = "%(root)s.%(device)s" % locals() 
     120            cmd = command % locals() 
     121            if os.path.exists(outfilename) and os.stat(outfilename).st_size : 
     122                sys.stdout.write("Skipping %(outfilename)s : already exists.\n" % locals()) 
     123            else :     
     124                sys.stdout.write("Generating %(outfilename)s " % locals()) 
     125                sys.stdout.flush() 
     126                os.system(cmd) 
     127                sys.stdout.write("\n") 
     128                 
     129            if not os.path.exists(outfilename) : 
     130                sys.stderr.write("ERROR : During the generation of %(outfilename)s\n" % locals()) 
     131            elif not os.stat(outfilename).st_size :     
     132                sys.stderr.write("ERROR : Unsupported driver, impossible to generate %(outfilename)s\n" % locals()) 
     133                os.remove(outfilename) 
     134            else :     
     135                self.results[outfilename] = { "command" : cmd, 
     136                                              "device" : "%s" % (devprefix + device), 
     137                                              "result" : None, 
     138                                              "details" : None, 
     139                                            } 
     140                 
     141    def genTestSuite(self) : 
     142        """Generate the testsuite.""" 
     143        root = "testsuite.%s" % self.md5sum 
     144        self.batchGeneration(self.inputfile, self.getAvailableDevices(),  
     145                                        root,  
     146                                        'gs -dQUIET -dBATCH -dNOPAUSE -dPARANOIDSAFER -sOutputFile="%(outfilename)s" -sDEVICE="%(device)s" "%(infilename)s"') 
     147                                     
     148        self.batchGeneration(self.inputfile, self.getAvailableIJSPrintClasses(),  
     149                                        "%(root)s.hpijs" % locals(),  
     150                                        'gs -dBATCH -dQUIET -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ijs -sIjsServer=hpijs -dIjsUseOutputFD -sDeviceManufacturer="HEWLETT-PACKARD" -sDeviceModel="%(device)s" -sOutputFile="%(outfilename)s" "%(infilename)s"') 
     151                 
     152    def computeSize(self, filename) :     
     153        """Computes the size in pages of a file in the testsuite.""" 
     154        answerfd = os.popen('pkpgcounter "%(filename)s" 2>/dev/null' % locals(), "r") 
     155        try : 
     156            try : 
     157                return int(answerfd.readline().strip()) 
     158            except (ValueError, TypeError) :     
     159                return 0 
     160        finally :         
     161            answerfd.close() 
     162         
     163    def runTests(self) : 
     164        """Launches the page counting tests against the testsuite.""" 
     165        masterfilename = self.inputfile 
     166        self.mastersize = mastersize = self.computeSize(masterfilename) 
     167        if not mastersize : 
     168            raise RuntimeError, "Unable to compute the size of the testsuite's master file %(masterfilename)s" % locals() 
     169        else :     
     170            sys.stdout.write("Master file's contains %(mastersize)i pages.\n" % locals()) 
     171        testsuite = glob.glob("testsuite.*") 
     172        testsuite.sort() 
     173        nbtests = len(testsuite) 
     174        for testfname in testsuite : 
     175            parts = testfname.split(".") 
     176            if len(parts) > 3 : 
     177                devname = ".".join(parts[2:]) 
     178            else :     
     179                devname = parts[-1] 
     180            result = self.results.setdefault(testfname, { "command" : "Unknown",  
     181                                                          "device" : devname,  
     182                                                          "result" : None,  
     183                                                          "details" : None }) 
     184            sys.stdout.write("Testing %(testfname)s ... " % locals()) 
    73185            sys.stdout.flush() 
    74             os.system(command % locals()) 
    75             sys.stdout.write("\n") 
    76              
    77         if not os.path.exists(outfilename) : 
    78             sys.stderr.write("ERROR while generating %(outfilename)s\n" % locals()) 
    79      
    80 def genTestSuite(infilename, root) : 
    81     """Generate the testsuite.""" 
    82     batchGeneration(infilename, getAvailableDevices(),  
    83                                 root,  
    84                                 'gs -dQUIET -dBATCH -dNOPAUSE -dPARANOIDSAFER -sOutputFile="%(outfilename)s" -sDEVICE="%(device)s" "%(infilename)s"') 
    85                                  
    86     batchGeneration(infilename, getAvailableIJSPrintClasses(),  
    87                                 "%(root)s.hpijs" % locals(),  
    88                                 'gs -dBATCH -dQUIET -dPARANOIDSAFER -dNOPAUSE -sDEVICE=ijs -sIjsServer=hpijs -dIjsUseOutputFD -sDeviceManufacturer="HEWLETT-PACKARD" -sDeviceModel="%(device)s" -sOutputFile="%(outfilename)s" "%(infilename)s"') 
    89              
    90 def computeSize(filename) :     
    91     """Computes the size in pages of a file in the testsuite.""" 
    92     answerfd = os.popen('pkpgcounter "%(filename)s" 2>/dev/null' % locals(), "r") 
    93     try : 
    94         try : 
    95             return int(answerfd.readline().strip()) 
    96         except (ValueError, TypeError) :     
    97             return 0 
    98     finally :         
    99         answerfd.close() 
    100      
    101 def runTests(masterfilename, root) : 
    102     """Launches the page counting tests against the testsuite.""" 
    103     mastersize = computeSize(masterfilename) 
    104     if not mastersize : 
    105         raise RuntimeError, "Unable to compute the size of the testsuite's master file %(masterfilename)s" % locals() 
    106     else :     
    107         sys.stdout.write("Master file's contains %(mastersize)i pages.\n" % locals()) 
    108     passed = failed = unsupported = 0 
    109     testsuite = glob.glob("%(root)s.*" % locals()) 
    110     testsuite.sort() 
    111     nbtests = len(testsuite) 
    112     for testfname in testsuite : 
    113         sys.stdout.write("Testing %(testfname)s ... " % locals()) 
    114         sys.stdout.flush() 
    115         size = computeSize(testfname) 
    116         if size != mastersize : 
    117             if not size : 
    118                 sys.stdout.write("ERROR : Unsupported file format\n") 
    119                 unsupported += 1 
    120             else :     
    121                 sys.stdout.write("WARN : Found %(size)i pages instead of %(mastersize)i\n" % locals()) 
    122                 failed += 1 
    123         else :     
    124             sys.stdout.write("OK\n") 
    125             passed += 1 
    126     sys.stdout.write("     Passed : %i/%i (%.2f%%)\n" % (passed, nbtests, 100.0 * passed / nbtests)) 
    127     sys.stdout.write("     Failed : %i/%i (%.2f%%)\n" % (failed, nbtests, 100.0 * failed / nbtests)) 
    128     sys.stdout.write("Unsupported : %i/%i (%.2f%%)\n" % (unsupported, nbtests, 100.0 * unsupported / nbtests)) 
    129              
     186            size = self.computeSize(testfname) 
     187            if size != mastersize : 
     188                if not size : 
     189                    result["result"] = "UNSUPPORTED" 
     190                    result["details"] = "Unsupported file format" 
     191                else :     
     192                    result["result"] = "FAILED" 
     193                    result["details"] = "Found %(size)i pages instead of %(mastersize)i\n" % locals() 
     194            else :     
     195                result["result"] = "SUPPORTED" 
     196                result["details"] = None 
     197            sys.stdout.write("%s\n" % result["result"])     
     198        self.supportedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "SUPPORTED"]) / nbtests 
     199        self.failedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "FAILED"]) / nbtests 
     200        self.unsupportedpct = 100.0 * len([True for r in self.results.values() if r["result"] == "UNSUPPORTED"]) / nbtests 
     201         
     202    def genHTMLReport(self, filename) : 
     203        """Generates an HTML report.""" 
     204        reportdate = "%s (UTC)" % time.asctime(time.gmtime(time.time())) 
     205        title = "pkpgcounter report for testsuite %s generated on %s" % (self.md5sum, reportdate) 
     206        out = open(filename, "w") 
     207        out.write("<html><head><title>%s</title></head><body>\n" % title) 
     208        out.write("<h3>%s</h3>\n" % title) 
     209        out.write("<ul>\n") 
     210        out.write("<li>Testsuite's MD5 checksum : <strong>%s</strong></li>\n" % self.md5sum) 
     211        out.write("<li>Testsuite contains : <strong>%i pages</strong></li>\n" % self.mastersize) 
     212        out.write("<li>Supported : <strong>%.2f%%</strong></li>\n" % self.supportedpct) 
     213        out.write("<li>Failed : <strong>%.2f%%</strong></li>\n" % self.failedpct) 
     214        out.write("<li>Unsupported : <strong>%.2f%%</strong></li>\n" % self.unsupportedpct) 
     215        out.write("</ul>\n") 
     216        out.write("<p><strong>Green</strong> means that pkpgcounter obtained the expected result</p>\n") 
     217        out.write("<p><strong>Orange</strong> means that pkpgcounter obtained an incorrect result</p>\n") 
     218        out.write("<p><strong>Red</strong> means that pkpgcounter doesn't recognize the input file's format</p>\n") 
     219        out.write('<table border="1"><tr bgcolor="gold"><th width="15%">Device</th><th width="25%">Details</th><th width="60%">Command line</th></tr>\n') 
     220        linecount = 0 
     221        keys = self.results.keys() 
     222        keys.sort() 
     223        for key in keys : 
     224            value = self.results[key] 
     225            linecount += 1     
     226            if not (linecount % 2) :     
     227                linecolor = "#DEDEDE" 
     228            else :     
     229                linecolor = "#FFFFFF" 
     230            out.write('<tr bgcolor="%s">\n' % linecolor)     
     231            if value["result"] == "SUPPORTED" : 
     232                color = "#00FF00" 
     233            elif value["result"] == "UNSUPPORTED" :     
     234                color = "#FF0000" 
     235            else :     
     236                color = "orange" 
     237            out.write('<td bgcolor="%s"><strong>%s</strong></td>\n' % (color, value["device"])) 
     238            out.write('<td>%s</td>\n' % (value["details"] or "&nbsp;")) 
     239            out.write('<td><em>%s</em></td>\n' % value["command"]) 
     240            out.write("</tr>\n") 
     241        out.write("</table></body></html>\n") 
     242        out.close() 
     243         
    130244def main() :         
    131245    """Main function.""" 
     
    133247        sys.argv.append("-") 
    134248    if len(sys.argv) != 2 : 
    135         sys.stderr.write("usage : gengstests.py [inputfile.ps]\n") 
     249        sys.stderr.write("usage : %s [inputfile.ps]\n" % sys.argv[0]) 
    136250        sys.exit(-1) 
    137251    else :     
    138         checksum = md5.new() # Ensures we'll recreate a new testsuite if input is different 
    139         infilename = sys.argv[1] 
    140         istemp = False 
    141         if infilename == "-" : 
    142             # Input is standard input, so we must use a temporary 
    143             # file to be able to loop over all available devices. 
    144             tmp = tempfile.NamedTemporaryFile(mode="w+b") 
    145             istemp = True 
    146             infilename = tmp.name 
    147             while True : 
    148                 data = sys.stdin.read(MEGABYTE) 
    149                 if not data : 
    150                     break 
    151                 tmp.write(data) 
    152                 checksum.update(data) 
    153             tmp.flush()     
    154         else :     
    155             checksum.update(infilename) 
    156         genTestSuite(infilename, "testsuite.%s" % checksum.hexdigest()) 
    157         runTests(infilename, "testsuite") 
    158              
    159         if istemp :     
    160             # Cleanly takes care of the temporary file 
    161             tmp.close() 
    162              
     252        testsuite = TestSuite(sys.argv[1]) 
     253        testsuite.genTestSuite() 
     254        testsuite.runTests() 
     255        testsuite.genHTMLReport("%s.html" % testsuite.md5sum) 
     256         
    163257if __name__ == "__main__" : 
    164258    sys.exit(main())