From accd3917f73ef5e77541d7125041cf8361fbe899 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Fonseca?= Date: Thu, 27 Oct 2011 12:13:03 +0100 Subject: [PATCH] Basic automation with CTest. --- .gitignore | 2 + CMakeLists.txt | 6 + apps/gl/CMakeLists.txt | 9 +- apps/gl/tri.ref.txt | 16 +++ driver.py | 241 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 apps/gl/tri.ref.txt create mode 100755 driver.py diff --git a/.gitignore b/.gitignore index 7c2ddbe..0f342ec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.pyc CMakeCache.txt CMakeFiles +CTestTestfile.cmake Makefile +Testing cmake_install.cmake results diff --git a/CMakeLists.txt b/CMakeLists.txt index 32c3029..f69714a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,5 +51,11 @@ if (MSVC) add_definitions (-wd4244) # conversion' conversion from 'type1' to 'type2', possible loss of data endif () + +set (APITRACE_BINARY_DIR "${CMAKE_BINARY_DIR}/.." CACHE PATH "apitrace build directory") + + +enable_testing() + add_subdirectory (apps) diff --git a/apps/gl/CMakeLists.txt b/apps/gl/CMakeLists.txt index a6be3d0..925261c 100644 --- a/apps/gl/CMakeLists.txt +++ b/apps/gl/CMakeLists.txt @@ -20,5 +20,12 @@ set (targets foreach (target ${targets}) add_executable (${subdir}_${target} ${target}.c) set_target_properties (${subdir}_${target} PROPERTIES OUTPUT_NAME ${target}) - install (TARGETS ${subdir}_${target} DESTINATION ${subdir}) + + add_test( + NAME ${subdir}_${target} + COMMAND python ${CMAKE_SOURCE_DIR}/driver.py + --build ${APITRACE_BINARY_DIR} + --ref-dump ${CMAKE_CURRENT_SOURCE_DIR}/${target}.ref.txt + ${CMAKE_CURRENT_BINARY_DIR}/${target} + ) endforeach (target) diff --git a/apps/gl/tri.ref.txt b/apps/gl/tri.ref.txt new file mode 100644 index 0000000..060dd44 --- /dev/null +++ b/apps/gl/tri.ref.txt @@ -0,0 +1,16 @@ +glClearColor(red = 0.3, green = 0.1, blue = 0.3, alpha = 0) +glViewport(x = 0, y = 0, width = 250, height = 250) +glMatrixMode(mode = GL_PROJECTION) +glLoadIdentity() +glOrtho(left = -1, right = 1, bottom = -1, top = 1, zNear = -0.5, zFar = 1000) +glMatrixMode(mode = GL_MODELVIEW) +glClear(mask = GL_COLOR_BUFFER_BIT) +glBegin(mode = GL_TRIANGLES) +glColor3f(red = 0.8, green = 0, blue = 0) +glVertex3f(x = -0.9, y = -0.9, z = -30) +glColor3f(red = 0, green = 0.9, blue = 0) +glVertex3f(x = 0.9, y = -0.9, z = -30) +glColor3f(red = 0, green = 0, blue = 0.7) +glVertex3f(x = 0, y = 0.9, z = -30) +glEnd() +glFlush() diff --git a/driver.py b/driver.py new file mode 100755 index 0000000..a78be5b --- /dev/null +++ b/driver.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +########################################################################## +# +# Copyright 2011 Jose Fonseca +# 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, sublicense, 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 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 NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS 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. +# +##########################################################################/ + +'''Common test suite code.''' + + +import math +import optparse +import os.path +import re +import signal +import subprocess +import sys +import time + + +def popen(command, *args, **kwargs): + if kwargs.get('cwd', None) is not None: + sys.stdout.write('cd %s && ' % kwargs['cwd']) + if 'env' in kwargs: + for name, value in kwargs['env'].iteritems(): + if value != os.environ.get(name, None): + sys.stdout.write('%s=%s ' % (name, value)) + sys.stdout.write(' '.join(command) + '\n') + sys.stdout.flush() + return subprocess.Popen(command, *args, **kwargs) + + +def _get_build_path(path): + if options.build is not None: + path = os.path.abspath(os.path.join(options.build, path)) + if not os.path.exists(path): + sys.stderr.write('error: %s does not exist\n' % path) + sys.exit(1) + return path + + +class TestCase: + + max_frames = None + + def __init__(self, name, args, cwd=None, build=None, results = '.'): + self.name = name + self.args = args + self.cwd = cwd + self.build = build + self.results = results + + if not os.path.exists(results): + os.makedirs(results) + + expected_dump = None + + def standalone(self): + p = popen(self.args, cwd=self.cwd) + p.wait() + if p.returncode: + sys.stdout.write('SKIP (application faied)\n') + sys.exit(0) + + def trace(self): + self.trace_file = os.path.abspath(os.path.join(self.results, self.name + '.trace')) + ld_preload = _get_build_path('glxtrace.so') + + env = os.environ.copy() + env['LD_PRELOAD'] = ld_preload + env['TRACE_FILE'] = self.trace_file + if self.max_frames is not None: + env['TRACE_FRAMES'] = str(self.max_frames) + + p = popen(self.args, env=env, cwd=self.cwd) + p.wait() + + if not os.path.exists(self.trace_file): + sys.stdout.write('FAIL (trace file missing)\n') + sys.exit(1) + + call_re = re.compile(r'^([0-9]+) (\w+)\(') + + def dump(self): + + cmd = [_get_build_path('tracedump'), '--color=never', self.trace_file] + p = popen(cmd, stdout=subprocess.PIPE) + + swapbuffers = 0 + flushes = 0 + + ref_line = '' + if self.ref_dump is not None: + ref = open(self.ref_dump, 'rt') + ref_line = ref.readline().rstrip() + for line in p.stdout: + line = line.rstrip() + mo = self.call_re.match(line) + assert mo + if mo: + call_no = int(mo.group(1)) + function_name = mo.group(2) + if function_name == 'glXSwapBuffers': + swapbuffers += 1 + if function_name in ('glFlush', 'glFinish'): + flushes += 1 + src_line = line[mo.start(2):] + sys.stdout.write(src_line + '\n') + if ref_line: + if src_line == ref_line: + ref_line = ref.readline().rstrip() + p.wait() + if p.returncode != 0: + sys.stdout.write('FAIL (tracedump)\n') + sys.exit(1) + if ref_line: + sys.stdout.write('FAIL expected %s\n' % ref_line) + + + def run(self): + self.standalone() + self.trace() + self.dump() + + sys.stdout.write('PASS\n') + return + + ref_prefix = os.path.abspath(os.path.join(self.results, self.name + '.ref.')) + src_prefix = os.path.join(self.results, self.name + '.src.') + diff_prefix = os.path.join(self.results, self.name + '.diff.') + + + if not os.path.isfile(trace): + sys.stdout.write('SKIP (no trace)\n') + return + args = [_get_build_path('glretrace')] + if swapbuffers: + args += ['-db'] + frames = swapbuffers + else: + args += ['-sb'] + frames = flushes + args += ['-s', src_prefix] + args += [trace] + p = popen(args, stdout=subprocess.PIPE) + image_re = re.compile(r'^Wrote (.*\.png)$') + images = [] + for line in p.stdout: + line = line.rstrip() + mo = image_re.match(line) + if mo: + image = mo.group(1) + if image.startswith(src_prefix): + image = image[len(src_prefix):] + images.append(image) + p.wait() + if p.returncode != 0: + sys.stdout.write('FAIL (glretrace)\n') + return + + for image in images: + ref_image = ref_prefix + image + src_image = src_prefix + image + diff_image = diff_prefix + image + + if not os.path.isfile(ref_image): + continue + assert os.path.isfile(src_image) + + comparer = Comparer(ref_image, src_image) + match = comparer.ae() + sys.stdout.write('%s: %s bits\n' % (image, comparer.precision())) + if not match: + comparer.write_diff(diff_image) + #report.add_snapshot(ref_image, src_image, diff_image) + sys.stdout.write('FAIL (snapshot)\n') + return + + + +def main(): + global options + + # Parse command line options + optparser = optparse.OptionParser( + usage='\n\t%prog [options] -- program [args] ...', + version='%%prog') + optparser.add_option( + '-B', '--build', metavar='PATH', + type='string', dest='build', default='..', + help='path to apitrace build') + optparser.add_option( + '-C', '--directory', metavar='PATH', + type='string', dest='cwd', default=None, + help='change to directory') + optparser.add_option( + '-R', '--results', metavar='PATH', + type='string', dest='results', default='results', + help='results directory [default=%default]') + optparser.add_option( + '--ref-dump', metavar='PATH', + type='string', dest='ref_dump', default=None, + help='reference dump') + + (options, args) = optparser.parse_args(sys.argv[1:]) + if not args: + optparser.error('program must be specified') + + test = TestCase( + name = os.path.basename(args[0]), + args = args, + cwd = options.cwd, + build = options.build, + results = options.results, + ) + test.ref_dump = options.ref_dump + + test.run() + + +if __name__ == '__main__': + main() -- 2.43.0