root / pkpgcounter / trunk / tests / gstests.py @ 537

Revision 537, 11.8 kB (checked in by jerome, 15 years ago)

Cleanly exits when the user does Ctrl+C.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3#
4# pkpgcounter : a generic Page Description Language parser
5#
6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20# $Id$
21#
22#
23
24"""This script generates a testsuite from a PostScript input file and ghostscript."""
25
26import sys
27import os
28import glob
29import md5
30import tempfile
31import time
32
33MEGABYTE = 1024 * 1024
34
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
62        else :   
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" : "See above", 
181                                                          "device" : devname, 
182                                                          "result" : None, 
183                                                          "details" : None })
184            sys.stdout.write("Testing %(testfname)s ... " % locals())
185            sys.stdout.flush()
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       
244def main() :       
245    """Main function."""
246    try :
247        if len(sys.argv) == 1 :
248            sys.argv.append("-")
249        if len(sys.argv) != 2 :
250            sys.stderr.write("usage : %s [inputfile.ps]\n" % sys.argv[0])
251            sys.exit(-1)
252        else :   
253            testsuite = TestSuite(sys.argv[1])
254            testsuite.genTestSuite()
255            testsuite.runTests()
256            testsuite.genHTMLReport("%s.html" % testsuite.md5sum)
257    except KeyboardInterrupt :       
258        sys.stderr.write("Interrupted at user's request !\n")
259       
260if __name__ == "__main__" :
261    sys.exit(main())
262       
Note: See TracBrowser for help on using the browser.