Synopsis - Cross-Reference

File: /tests/QMTest/synopsis_test.py
  1#!/usr/bin/env python
  2#
  3# Copyright (C) 2003 Stefan Seefeld
  4# All rights reserved.
  5# Licensed to the public under the terms of the GNU LGPL (>= 2),
  6# see the file COPYING for details.
  7#
  8
  9from qm.fields import TextField
 10from qm.executable import RedirectedExecutable
 11from qm.test.test import Test
 12from qm.test.resource import Resource
 13from qm.test.result import Result
 14
 15import os, sys, string, re
 16import difflib
 17
 18class APITest(Test):
 19   """Compile and run a test to validate the C++ API."""
 20
 21   arguments = [TextField(name="src", description="The source file."),
 22                TextField(name="exe", description="The executable file."),
 23                TextField(name="expected", description="The expected output file.")]
 24
 25   def compile(self, context, result):
 26      if not os.path.isdir(os.path.dirname(self.exe)):
 27         os.makedirs(os.path.dirname(self.exe))
 28
 29      CXX = context.get('CXX', 'c++')
 30      CPPFLAGS = context.get('CPPFLAGS', '')
 31      CXXFLAGS = context.get('CXXFLAGS', '')
 32      LDFLAGS = context.get('LDFLAGS', '')
 33      LIBS = context.get('LIBS', '')
 34      command = '%s %s %s %s -o %s %s %s'%(CXX, CPPFLAGS, CXXFLAGS, LDFLAGS,
 35                                           self.exe, self.src, LIBS)
 36      compiler = RedirectedExecutable()
 37      status = compiler.Run(string.split(command))
 38      if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
 39         return self.exe
 40      else:
 41         result.Fail('compilation failed',
 42                     {'synopsis_test.error': compiler.stderr,
 43                      'synopsis_test.command': command})
 44         return None      
 45
 46   def Run(self, context, result):
 47      exe = self.compile(context, result)
 48      if not exe: return
 49      test = RedirectedExecutable()
 50      status = test.Run([exe])
 51      if not os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
 52         result.Fail('program exit value : %i'%os.WEXITSTATUS(status))
 53         if test.stderr: result['synopsis_test.error'] = test.stderr
 54
 55      try:
 56         expected = open(self.expected, 'r').readlines()
 57         output = test.stdout.split('\n')
 58      except IOError, error:
 59         result.Fail('error reading expected output',
 60                     {'synopsis_test.error': error.strerror})
 61         return
 62      if expected and not output:
 63         result.Fail('program did not generate output')
 64      elif expected and expected != output:
 65         diff = ''.join(difflib.unified_diff(expected, output))
 66         expected = ''.join(expected)
 67         result.Fail('incorrect output',
 68                     {'synopsis_test.expected': result.Quote(expected),
 69                      'synopsis_test.output': result.Quote(test.stdout),
 70                      'synopsis_test.diff': result.Quote(diff)})
 71
 72
 73class ProcessorTest(Test):
 74   """Process an input file with a synopsis script and
 75   compare the output."""
 76
 77   arguments = [TextField(name="srcdir", description="The source directory."),
 78                TextField(name="synopsis", description="The synopsis script."),
 79                TextField(name="input", description="The input files."),
 80                TextField(name="output", description="The output file."),
 81                TextField(name="expected", description="The output file.")]
 82
 83   def run_processor(self, context, result):
 84
 85      input = map(lambda x:os.path.join(self.srcdir, x), self.input)
 86      if not os.path.isdir(os.path.dirname(self.output)):
 87         os.makedirs(os.path.dirname(self.output))
 88
 89      command = 'python %s parse --output=%s %s'%(self.synopsis,
 90                                                  self.output,
 91                                                  string.join(self.input, ' '))
 92      # Make sure the modules from the current working dir are used.
 93      #os.environ['PYTHONPATH'] = os.path.join(self.srcdir, os.pardir)
 94      script = RedirectedExecutable(60) # 1 minute ought to be enough...
 95      status = script.Run(string.split(command))
 96      if status != 0:
 97         result.Fail('unable to run',
 98                     {'synopsis_test.command': result.Quote(command),
 99                      'synopsis_test.error': result.Quote(script.stderr)})
100      return status == 0
101
102   def Run(self, context, result):
103
104      if self.run_processor(context, result):
105         try:
106            expected = open(self.expected, 'r').readlines()
107         except IOError, error:
108            result.Fail('error reading expected output',
109                        {'synopsis_test.error': error.strerror})
110            return
111         output = open(self.output, 'r').readlines()
112         if expected != output:
113            diff = ''.join(difflib.unified_diff(expected, output))
114            expected = ''.join(expected)
115            output = ''.join(output)
116            result.Fail('incorrect output',
117                        {'synopsis_test.expected': result.Quote(expected),
118                         'synopsis_test.output': result.Quote(output),
119                         'synopsis_test.diff': result.Quote(diff)})
120
121class CxxResource(Resource):
122   """build the executables the CxxTests all depend on."""
123
124   arguments = [TextField(name="MAKE", description="The make tool."),
125                TextField(name="exe", description="The executable file.")]
126
127   def compile(self, context, result):
128
129      make = os.environ.get('MAKE', 'make')
130      command = '%s -C Cxx %s '%(make, self.exe)
131      compiler = RedirectedExecutable()
132      status = compiler.Run(string.split(command))
133      if os.WIFEXITED(status) and os.WEXITSTATUS(status) == 0:
134         return self.exe
135      else:
136         result.Fail('compilation failed',
137                     {'synopsis_test.error': compiler.stderr,
138                      'synopsis_test.command': command})
139         return None      
140
141   def SetUp(self, context, result):
142
143      self.compile(context, result)
144
145class CxxTest(Test):
146   """Process an input file with a processor and compare the output.
147   If the processor doesn't exist yet, attempt to build it."""
148
149   arguments = [TextField(name="applet", description="The applet."),
150                TextField(name="srcdir", description="The source directory."),
151                TextField(name="input", description="The input files."),
152                TextField(name="output", description="The output files."),
153                TextField(name="expected", description="The expected output file.")]
154
155   _ld_paths = re.compile(r'-L\s*\S+')
156
157   def run_applet(self, context, result):
158
159      input = map(lambda x:os.path.join(self.srcdir, x), self.input)
160      if not os.path.isdir(os.path.dirname(self.output)):
161         os.makedirs(os.path.dirname(self.output))
162
163      # Make sure we see the right libraries during program loading.
164      LIBS=context.get('LIBS', '')
165      LD_PATHS=[p[2:].strip() for p in CxxTest._ld_paths.findall(LIBS)]
166      LD_LIBRARY_PATH = ':'.join(LD_PATHS)
167      LD_LIBRARY_PATH += ':' + os.environ.get('LD_LIBRARY_PATH', '')
168      os.environ['LD_LIBRARY_PATH'] = LD_LIBRARY_PATH
169
170      test = RedirectedExecutable()
171      command = '%s %s %s'%(self.applet, self.output, self.input)
172      status = test.Run(command.split())
173      if os.WIFSIGNALED(status):
174         result.Fail('program killed with signal %i'%os.WTERMSIG(status),
175                     {'synopsis_test.command': command})
176         
177      elif os.WIFEXITED(status) and os.WEXITSTATUS(status) != 0:
178         result.Fail('program exit value : %i'%os.WEXITSTATUS(status),
179                     {'synopsis_test.command': command})
180         if test.stderr: result['synopsis_test.error'] = test.stderr
181
182      else:
183         try:
184            expected = open(self.expected, 'r').readlines()
185         except IOError, error:
186            result.Fail('error reading expected output',
187                        {'synopsis_test.error': error.strerror})
188            return
189         try:
190            output = open(self.output, 'r').readlines()
191         except IOError, error:
192            result.Fail('error reading actual output',
193                        {'synopsis_test.error': error.strerror})
194            return
195         
196         if expected != output:
197            diff = ''.join(difflib.unified_diff(expected, output))
198            expected = ''.join(expected)
199            output = ''.join(output)
200            result.Fail('incorrect output',
201                        {'synopsis_test.expected': result.Quote(expected),
202                         'synopsis_test.output': result.Quote(output),
203                         'synopsis_test.diff': result.Quote(diff)})
204
205   def Run(self, context, result):
206
207      self.run_applet(context, result)