Salome HOME
src.xmlManager escapeSequence
[tools/sat.git] / AllTestLauncherSat.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Copyright (C) 2008-2018  CEA/DEN
5
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
10
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # Lesser General Public License for more details.
15
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 # See http://www.salome-platform.org or email : webmaster.salome@opencascade.com
21
22
23 """
24 Test all SAT unittest files (test*.py) existing in subdirectories of a root directory
25
26 | Example:
27 | >> AllTestLauncherSat.py --rootPath='.' --pattern='test_???_*.py'
28 """
29
30 """
31 #### base of algorithm functionality
32
33 #see http://www.voidspace.org.uk/python/articles/introduction-to-unittest.shtml
34
35 import unittest
36
37 import test_something
38 import test_something2
39 import test_something3
40
41 loader = unittest.TestLoader()
42
43 suite = loader.loadTestsFromModule(test_something)
44 suite.addTests(loader.loadTestsFromModule(test_something2))
45 suite.addTests(loader.loadTestsFromModule(test_something3))
46
47 runner = unittest.TextTestRunner(verbosity=2)
48 result = runner.run(suite)
49 """
50
51
52 import os
53 import sys
54 import unittest
55 import traceback
56 import argparse as AP
57
58 import glob
59 import fnmatch
60 import pprint as PP #pretty print
61
62 debug = False
63 verboseImport = True
64
65
66 # get path to origin sources
67 defaultdir  = os.path.dirname(os.path.realpath(__file__))
68 # get path to salomeTools sources
69 satdir = defaultdir
70 srcdir = os.path.join(satdir, 'src')
71 cmdsdir = os.path.join(satdir, 'commands')
72
73 # Make the src & commands package accessible from all code
74 sys.path.insert(0, satdir)
75 sys.path.insert(0, srcdir) # TODO remove that
76 sys.path.insert(0, cmdsdir) # TODO remove that
77
78 _user = os.environ['USER']
79 # wambeke is christian at home
80 _developpers = ["christian", "wambeke",] #  ...who wants
81
82 def errPrint(aStr):
83   """stderr to avoid write in html or xml file log message"""
84   sys.stderr.write(aStr + '\n')
85
86 try:
87   import unittestpy.HTMLTestRunner as HTST
88 except:
89   HTST = None
90   errPrint("""
91 WARNING: no HTML output available.
92          try find 'test/unittestpy/HTMLTestRunner.py'.
93 """)
94
95
96 try:
97   import xmlrunner as XTST
98 except:
99   XTST = None
100   errPrint("""
101 WARNING: no XML output available for unittest.
102          try 'pip install unittest-xml-reporting'.
103 """)
104
105
106 ###################################################################
107 def locate(pattern, root=os.curdir):
108   """
109   Locate all files matching supplied filename pattern in and below
110   supplied root directory.
111   """
112   result = []
113   for path, dirs, files in os.walk(os.path.abspath(root)):
114     for filename in fnmatch.filter(files, pattern):
115       result.append( os.path.join(path, filename) )
116   return result
117
118 def printEnv(search=""):
119   """
120   list all environment variables which name contains search string
121   example: 
122     import AllTestLauncher as ATL
123     ATL.printEnv("ROOT_")
124   """
125   env=os.environ
126   for i in sorted(env):
127     if search in i:
128       print(i) 
129
130 def grepInEnv(search=""):
131   """
132   list all environment variables which contains search string
133   example: 
134     import AllTestLauncher as ATL
135     ATL.grepInEnv("XDATA")
136   """
137   env=os.environ
138   for i in sorted(env):
139      done=False
140      for j in env[i].split(":"):
141        if search in j:
142            if not done:
143              print(i+" contains ") 
144              done=True
145            print("  "+j)
146       
147 def format_exception(msg, limit=None, trace=None):
148     """
149     Format a stack trace and the exception information.
150     as traceback.format_exception(),
151     with all traceback only if user in ._developpers
152     """
153     etype, value, tb = sys.exc_info()
154     if _user in _developpers:
155       res = "\n" + msg
156       if tb:
157           res += "\nTraceback (most recent call last):\n"
158           res += "".join(traceback.format_tb(tb, limit)) #[:-1])
159       res += "\n<"
160       res += "\n".join(traceback.format_exception_only(etype, value))
161       return res
162     else:
163       res = "\n" + msg
164       if tb:
165           res += "\nTraceback:\n"
166           res += "".join(traceback.format_tb(tb, limit)[-1:]) #[:-1])
167       res += "\n<"
168       res += "".join(traceback.format_exception_only(etype, value))
169       return res
170
171 ###################################################################
172 def runOnArgs(args):
173   """
174   launch tests on args.pattern files
175   """
176   fromFileOrPath = args.rootPath
177   fileTestPattern = args.pattern
178   if fromFileOrPath == None:
179     directory, name = os.path.split( os.path.realpath( __file__ ) )
180   else:
181     if os.path.isdir(fromFileOrPath):
182       directory, name = (fromFileOrPath, None)
183       fileTestPatternCurrent = fileTestPattern
184     elif os.path.isfile(fromFileOrPath):
185       directory, name = os.path.split( os.path.realpath( fromFileOrPath ) )
186       fileTestPatternCurrent = name
187     else:
188       mess = "Cannot get file or directory '%s'" % fromFileOrPath
189       errPrint("ERROR: " + mess)
190       return None
191       #raise Exception("Cannot get file or directory '%s'" % fromFileOrPath)
192
193   #files = glob.glob(os.path.join(directory, "*Test.py"))
194   files = sorted(locate(fileTestPatternCurrent, directory))
195
196   filesForTest={}
197
198   for aFile in files:
199     aDir, aName = os.path.split(aFile)
200     aImport, ext = os.path.splitext(aName)
201     
202     try:
203       if aFile in list(filesForTest.keys()):
204         print("WARNING: imported yet: "+aFile)
205       else:
206         sys.path.insert(0, aDir)
207         done = True
208         if verboseImport: errPrint("try import '%s'" % aImport)
209         aModule = __import__(aImport, globals(), locals(), []) 
210         del sys.path[0]
211         done = False
212         filesForTest[aFile] = (aImport, aModule)
213     except Exception as e:
214       if done: 
215         del sys.path[0] #attention of sys.path appends
216         done = False
217       msg = "ERROR: AllTestLauncher: import '%s':" % aFile
218       err = format_exception(msg)
219       errPrint(err)
220       continue
221
222   listfilesForTest = sorted(filesForTest.keys())
223   result = None
224
225   errPrint("AllTestLauncher test files:\n %s" % PP.pformat(listfilesForTest))
226   
227   if len(listfilesForTest) == 0: 
228     if debug: errPrint("WARNING: AllTestLauncher: empty list of test files")
229     return None
230
231   loader = unittest.TestLoader()
232   suite = None
233
234   for i,k in enumerate(listfilesForTest):
235     if debug: errPrint("Test: %s %s" % (i, k))
236     if i == 0:
237       suite = loader.loadTestsFromModule( filesForTest[k][1] )
238       pass
239     else:
240       suite.addTests( loader.loadTestsFromModule( filesForTest[k][1] ) )
241       pass
242
243   if args.type == "std": 
244     runner = unittest.TextTestRunner(verbosity=args.verbosity)
245   elif args.type == "html": 
246     runner = HTST.HTMLTestRunner(verbosity=args.verbosity, )
247   elif args.type == "xml": 
248     if args.name == 'stdout':
249       #all-in-one xml output at 'sys.stdout' for pipe redirection
250       runner = XTST.XMLTestRunner(verbosity=args.verbosity, output=sys.stdout)
251     else:
252       #one file xml per test in suite in args.name directory
253       runner = XTST.XMLTestRunner(verbosity=args.verbosity, output=args.name)
254   else:
255     errPrint("ERROR: unknown type of output: '%s'" % args.type)
256     return None    
257     
258   if suite != None: result = runner.run(suite)
259   return result
260
261 ###################################################################
262 def runFromEnvVar(envVar, fileTestPattern="*Test.py"):
263   """
264   example: 
265     import AllTestLauncher as ATL
266     ATL.runFromEnvVar("MICROGEN_ROOT_DIR")
267     ATL.runFromEnvVar("MICROGEN_ROOT_DIR", "aggregate_*GJKTest.py")
268   """
269   env=os.environ
270   res = []
271   for i in sorted(env):
272     if envVar in i:
273       res.append(i)
274   if len(res) > 1:
275     mess = "multiple environment variable for '%s': %s" % (envVar, str(res))
276     errPrint("ERROR: " + mess)
277     return None
278   if len(res) < 1:
279     mess = "no environment variable for '%s'" % (envVar)
280     errPrint("ERROR: " + mess)
281     return None
282   res = res[0]
283   tmp = env[res].split(":")
284   if len(tmp) > 1:
285     mess = "need only one path in environment variable '%s'" % (res)
286     errPrint("ERROR: " + mess)
287     return None  
288   run(fromFileOrPath=env[res], fileTestPattern=fileTestPattern)
289
290
291 ###################################################################
292 def getParser():
293   parser = AP.ArgumentParser(description='launch All salomeTools python tests', argument_default=None)
294
295   parser.add_argument(
296     '-d', '--debug', 
297     help='set debug mode, more verbose',
298     action='store_true',
299   )
300   parser.add_argument(
301     '-v', '--verbosity', 
302     help='set verbosity of unittests [0|1|2...]',
303     default=2,
304     metavar='int'
305   )
306   parser.add_argument(
307     '-r', '--rootPath', 
308     help="""\
309 dir name with absolute or relative path stand for root directory
310 of recursive searching unittest python files
311 """,
312    default=defaultdir,
313    metavar='dirPath'
314   )
315   parser.add_argument(
316     '-p', '--pattern', 
317     help="file pattern for unittest files ['test_*.py'|'*Test.py'...]",
318     default="test_???_*.py", # as alphabetical ordered test site
319     metavar='filePattern'
320   )
321   parser.add_argument(
322     '-t', '--type', 
323     help="type of output: ['std'(standart ascii)|'xml'|'html']",
324     default="std",
325     choices=['std', 'xml', 'html'],
326     metavar='outputType'
327   )
328   parser.add_argument(
329     '-n', '--name', 
330     help="""\
331 (only for type xml)
332 name of directory output: ['test_reports'|...].
333 If name = 'stdout' then all-in-one xml output at 'sys.stdout'. For pipe redirection:
334 '>> AllTestLauncher.py -t xml -n stdout > tmp.xml'
335 """,
336     default="test_reports",
337     metavar='dirName'
338   )
339   return parser
340
341 #export PATH=defaultdir:${PATH}
342
343 ###################################################################
344 if __name__ == '__main__':
345   # Make the src & command package accessible from all code
346   # as export PYTHONPATH=defaultdir:${PYTHONPATH}
347   # https://docs.python.org/2/library/os.html
348   # On some platforms, including FreeBSD and Mac OS X, 
349   # setting environ may cause memory leak
350   # so use sys.path
351   # errPrint("INFO    : AllTestLauncher sys.path:\n'%s'" % PP.pformat(sys.path)
352   if defaultdir not in sys.path[0]:
353     sys.path.insert(0, defaultdir)
354     errPrint("WARNING : sys.path prepend '%s'\n" % defaultdir)
355
356   args = getParser().parse_args(sys.argv[1:])
357   debug = args.debug
358   directory = os.path.realpath(args.rootPath)
359   if debug: print("INFO: args:\n  %s" % PP.pformat(args))
360   sys.path.insert(0, directory) #supposed to be root of a package
361   
362   runOnArgs(args)
363
364