diff options
Diffstat (limited to 'src/gallium/tests/python/tests/base.py')
-rwxr-xr-x | src/gallium/tests/python/tests/base.py | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/src/gallium/tests/python/tests/base.py b/src/gallium/tests/python/tests/base.py new file mode 100755 index 0000000000..d8cf84db36 --- /dev/null +++ b/src/gallium/tests/python/tests/base.py @@ -0,0 +1,399 @@ +#!/usr/bin/env python +########################################################################## +# +# Copyright 2009 VMware, Inc. +# Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. +# All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sub license, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice (including the +# next paragraph) shall be included in all copies or substantial portions +# of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +########################################################################## + + +"""Base classes for tests. + +Loosely inspired on Python's unittest module. +""" + + +import os.path +import sys + +from gallium import * + + +# Enumerate all pixel formats +formats = {} +for name, value in globals().items(): + if name.startswith("PIPE_FORMAT_") and isinstance(value, int) and name not in ("PIPE_FORMAT_NONE", "PIPE_FORMAT_COUNT"): + formats[value] = name + +def make_image(width, height, rgba): + import Image + outimage = Image.new( + mode='RGB', + size=(width, height), + color=(0,0,0)) + outpixels = outimage.load() + for y in range(0, height): + for x in range(0, width): + offset = (y*width + x)*4 + r, g, b, a = [int(min(max(rgba[offset + ch], 0.0), 1.0)*255) for ch in range(4)] + outpixels[x, y] = r, g, b + return outimage + +def save_image(width, height, rgba, filename): + outimage = make_image(width, height, rgba) + outimage.save(filename, "PNG") + +def show_image(width, height, **rgbas): + import Tkinter as tk + from PIL import Image, ImageTk + + root = tk.Tk() + + x = 64 + y = 64 + + labels = rgbas.keys() + labels.sort() + for i in range(len(labels)): + label = labels[i] + outimage = make_image(width, height, rgbas[label]) + + if i: + window = tk.Toplevel(root) + else: + window = root + window.title(label) + image1 = ImageTk.PhotoImage(outimage) + w = image1.width() + h = image1.height() + window.geometry("%dx%d+%d+%d" % (w, h, x, y)) + panel1 = tk.Label(window, image=image1) + panel1.pack(side='top', fill='both', expand='yes') + panel1.image = image1 + x += w + 2 + + root.mainloop() + + +class TestFailure(Exception): + + pass + +class TestSkip(Exception): + + pass + + +class Test: + + def __init__(self): + pass + + def _run(self, result): + raise NotImplementedError + + def run(self): + result = TestResult() + self._run(result) + result.report() + + def assert_rgba(self, ctx, surface, x, y, w, h, expected_rgba, pixel_tol=4.0/256, surface_tol=0.85): + total = h*w + different = ctx.surface_compare_rgba(surface, x, y, w, h, expected_rgba, tol=pixel_tol) + if different: + sys.stderr.write("%u out of %u pixels differ\n" % (different, total)) + + if float(total - different)/float(total) < surface_tol: + if 0: + rgba = FloatArray(h*w*4) + ctx.surface_read_rgba(surface, x, y, w, h, rgba) + show_image(w, h, Result=rgba, Expected=expected_rgba) + save_image(w, h, rgba, "result.png") + save_image(w, h, expected_rgba, "expected.png") + #sys.exit(0) + + raise TestFailure + + +class TestCase(Test): + + tags = () + + def __init__(self, dev, **kargs): + Test.__init__(self) + self.dev = dev + self.__dict__.update(kargs) + + def description(self): + descriptions = [] + for tag in self.tags: + value = self.get(tag) + if value is not None and value != '': + descriptions.append(tag + '=' + str(value)) + return ' '.join(descriptions) + + def get(self, tag): + try: + method = getattr(self, '_get_' + tag) + except AttributeError: + return getattr(self, tag, None) + else: + return method() + + def _get_target(self): + return { + PIPE_TEXTURE_1D: "1d", + PIPE_TEXTURE_2D: "2d", + PIPE_TEXTURE_3D: "3d", + PIPE_TEXTURE_CUBE: "cube", + }[self.target] + + def _get_format(self): + name = formats[self.format] + if name.startswith('PIPE_FORMAT_'): + name = name[12:] + name = name.lower() + return name + + def _get_face(self): + if self.target == PIPE_TEXTURE_CUBE: + return { + PIPE_TEX_FACE_POS_X: "+x", + PIPE_TEX_FACE_NEG_X: "-x", + PIPE_TEX_FACE_POS_Y: "+y", + PIPE_TEX_FACE_NEG_Y: "-y", + PIPE_TEX_FACE_POS_Z: "+z", + PIPE_TEX_FACE_NEG_Z: "-z", + }[self.face] + else: + return '' + + def test(self): + raise NotImplementedError + + def _run(self, result): + result.test_start(self) + try: + self.test() + except KeyboardInterrupt: + raise + except TestSkip: + result.test_skipped(self) + except TestFailure: + result.test_failed(self) + else: + result.test_passed(self) + + +class TestSuite(Test): + + def __init__(self, tests = None): + Test.__init__(self) + if tests is None: + self.tests = [] + else: + self.tests = tests + + def add_test(self, test): + self.tests.append(test) + + def _run(self, result): + for test in self.tests: + test._run(result) + + +class TestResult: + + def __init__(self): + self.tests = 0 + self.passed = 0 + self.skipped = 0 + self.failed = 0 + + self.names = ['result'] + self.types = ['pass skip fail'] + self.rows = [] + + def test_start(self, test): + sys.stdout.write("Running %s...\n" % test.description()) + sys.stdout.flush() + self.tests += 1 + + def test_passed(self, test): + sys.stdout.write("PASS\n") + sys.stdout.flush() + self.passed += 1 + self.log_result(test, 'pass') + + def test_skipped(self, test): + sys.stdout.write("SKIP\n") + sys.stdout.flush() + self.skipped += 1 + self.log_result(test, 'skip') + + def test_failed(self, test): + sys.stdout.write("FAIL\n") + sys.stdout.flush() + self.failed += 1 + self.log_result(test, 'fail') + + def log_result(self, test, result): + row = ['']*len(self.names) + + # add result + assert self.names[0] == 'result' + assert result in ('pass', 'skip', 'fail') + row[0] = result + + # add tags + for tag in test.tags: + value = test.get(tag) + + # infer type + if value is None: + continue + elif isinstance(value, (int, float)): + value = str(value) + type = 'c' # continous + elif isinstance(value, basestring): + type = 'd' # discrete + else: + assert False + value = str(value) + type = 'd' # discrete + + # insert value + try: + col = self.names.index(tag, 1) + except ValueError: + self.names.append(tag) + self.types.append(type) + row.append(value) + else: + row[col] = value + assert self.types[col] == type + + self.rows.append(row) + + def report(self): + sys.stdout.write("%u tests, %u passed, %u skipped, %u failed\n\n" % (self.tests, self.passed, self.skipped, self.failed)) + sys.stdout.flush() + + name, ext = os.path.splitext(os.path.basename(sys.argv[0])) + + tree = self.report_tree(name) + self.report_junit(name, stdout=tree) + + def report_tree(self, name): + filename = name + '.tsv' + stream = file(filename, 'wt') + + # header + stream.write('\t'.join(self.names) + '\n') + stream.write('\t'.join(self.types) + '\n') + stream.write('class\n') + + # rows + for row in self.rows: + if row[0] == 'skip': + continue + row += ['']*(len(self.names) - len(row)) + stream.write('\t'.join(row) + '\n') + + stream.close() + + # See http://www.ailab.si/orange/doc/ofb/c_otherclass.htm + try: + import orange + import orngTree + except ImportError: + sys.stderr.write('Install Orange from http://www.ailab.si/orange/ for a classification tree.\n') + return None + + data = orange.ExampleTable(filename) + + tree = orngTree.TreeLearner(data, sameMajorityPruning=1, mForPruning=2) + + orngTree.printTxt(tree, maxDepth=4) + + text_tree = orngTree.dumpTree(tree) + + file(name + '.txt', 'wt').write(text_tree) + + orngTree.printDot(tree, fileName=name+'.dot', nodeShape='ellipse', leafShape='box') + + return text_tree + + def report_junit(self, name, stdout=None, stderr=None): + """Write test results in ANT's junit XML format, to use with Hudson CI. + + See also: + - http://fisheye.hudson-ci.org/browse/Hudson/trunk/hudson/main/core/src/test/resources/hudson/tasks/junit + - http://www.junit.org/node/399 + - http://wiki.apache.org/ant/Proposals/EnhancedTestReports + """ + + stream = file(name + '.xml', 'wt') + + stream.write('<?xml version="1.0" encoding="UTF-8" ?>\n') + stream.write('<testsuite name="%s">\n' % self.escape_xml(name)) + stream.write(' <properties>\n') + stream.write(' </properties>\n') + + names = self.names[1:] + + for row in self.rows: + + test_name = ' '.join(['%s=%s' % pair for pair in zip(self.names[1:], row[1:])]) + + stream.write(' <testcase name="%s">\n' % (self.escape_xml(test_name))) + + result = row[0] + if result == 'pass': + pass + elif result == 'skip': + stream.write(' <skipped/>\n') + else: + stream.write(' <failure/>\n') + + stream.write(' </testcase>\n') + + if stdout: + stream.write(' <system-out>%s</system-out>\n' % self.escape_xml(stdout)) + if stderr: + stream.write(' <system-err>%s</system-err>\n' % self.escape_xml(stderr)) + + stream.write('</testsuite>\n') + + stream.close() + + def escape_xml(self, s): + '''Escape a XML string.''' + s = s.replace('&', '&') + s = s.replace('<', '<') + s = s.replace('>', '>') + s = s.replace('"', '"') + s = s.replace("'", ''') + return s + |