Salome HOME
[FORUM 505] prepare command fix on windows
[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 import src
62
63 debug = False
64 verboseImport = True
65
66
67 # get path to origin sources
68 defaultdir  = os.path.dirname(os.path.realpath(__file__))
69 # get path to salomeTools sources
70 satdir = defaultdir
71 srcdir = os.path.join(satdir, 'src')
72 cmdsdir = os.path.join(satdir, 'commands')
73
74 # Make the src & commands package accessible from all code
75 sys.path.insert(0, satdir)
76 sys.path.insert(0, srcdir) # TODO remove that
77 sys.path.insert(0, cmdsdir) # TODO remove that
78
79 _user = src.architecture.get_user()
80 # wambeke is christian at home
81 _developers = ["christian", "wambeke",] #  ...who wants
82
83 def errPrint(aStr):
84   """stderr to avoid write in html or xml file log message"""
85   sys.stderr.write(aStr + '\n')
86
87 try:
88   import unittestpy.HTMLTestRunner as HTST
89 except Exception:
90   HTST = None
91   errPrint("""
92 WARNING: no HTML output available.
93          try find 'test/unittestpy/HTMLTestRunner.py'.
94 """)
95
96
97 try:
98   import xmlrunner as XTST
99 except Exception:
100   XTST = None
101   errPrint("""
102 WARNING: no XML output available for unittest.
103          try 'pip install unittest-xml-reporting'.
104 """)
105
106
107 ###################################################################
108 def locate(pattern, root=os.curdir):
109   """
110   Locate all files matching supplied filename pattern in and below
111   supplied root directory.
112   """
113   result = []
114   for path, dirs, files in os.walk(os.path.abspath(root)):
115     for filename in fnmatch.filter(files, pattern):
116       result.append( os.path.join(path, filename) )
117   return result
118
119 def printEnv(search=""):
120   """
121   list all environment variables which name contains search string
122   example: 
123     import AllTestLauncher as ATL
124     ATL.printEnv("ROOT_")
125   """
126   env=os.environ
127   for i in sorted(env):
128     if search in i:
129       print(i) 
130
131 def grepInEnv(search=""):
132   """
133   list all environment variables which contains search string
134   example: 
135     import AllTestLauncher as ATL
136     ATL.grepInEnv("XDATA")
137   """
138   env=os.environ
139   for i in sorted(env):
140      done=False
141      for j in env[i].split(":"):
142        if search in j:
143            if not done:
144              print(i+" contains ") 
145              done=True
146            print("  "+j)
147       
148 def format_exception(msg, limit=None, trace=None):
149     """
150     Format a stack trace and the exception information.
151     as traceback.format_exception(),
152     with all traceback only if user in ._developers
153     """
154     etype, value, tb = sys.exc_info()
155     if _user in _developers:
156       res = "\n" + msg
157       if tb:
158           res += "\nTraceback (most recent call last):\n"
159           res += "".join(traceback.format_tb(tb, limit)) #[:-1])
160       res += "\n<"
161       res += "\n".join(traceback.format_exception_only(etype, value))
162       return res
163     else:
164       res = "\n" + msg
165       if tb:
166           res += "\nTraceback:\n"
167           res += "".join(traceback.format_tb(tb, limit)[-1:]) #[:-1])
168       res += "\n<"
169       res += "".join(traceback.format_exception_only(etype, value))
170       return res
171
172 ###################################################################
173 def runOnArgs(args):
174   """
175   launch tests on args.pattern files
176   """
177   fromFileOrPath = args.rootPath
178   fileTestPattern = args.pattern
179   if fromFileOrPath == None:
180     directory, name = os.path.split( os.path.realpath( __file__ ) )
181   else:
182     if os.path.isdir(fromFileOrPath):
183       directory, name = (fromFileOrPath, None)
184       fileTestPatternCurrent = fileTestPattern
185     elif os.path.isfile(fromFileOrPath):
186       directory, name = os.path.split( os.path.realpath( fromFileOrPath ) )
187       fileTestPatternCurrent = name
188     else:
189       mess = "Cannot get file or directory '%s'" % fromFileOrPath
190       errPrint("ERROR: " + mess)
191       return None
192       #raise Exception("Cannot get file or directory '%s'" % fromFileOrPath)
193
194   #files = glob.glob(os.path.join(directory, "*Test.py"))
195   files = sorted(locate(fileTestPatternCurrent, directory))
196
197   filesForTest={}
198
199   for aFile in files:
200     aDir, aName = os.path.split(aFile)
201     aImport, ext = os.path.splitext(aName)
202     
203     try:
204       if aFile in list(filesForTest.keys()):
205         print("WARNING: imported yet: "+aFile)
206       else:
207         sys.path.insert(0, aDir)
208         done = True
209         if verboseImport: errPrint("try import '%s'" % aImport)
210         aModule = __import__(aImport, globals(), locals(), []) 
211         del sys.path[0]
212         done = False
213         filesForTest[aFile] = (aImport, aModule)
214     except Exception as e:
215       if done: 
216         del sys.path[0] #attention of sys.path appends
217         done = False
218       msg = "ERROR: AllTestLauncher: import '%s':" % aFile
219       err = format_exception(msg)
220       errPrint(err)
221       continue
222
223   listfilesForTest = sorted(filesForTest.keys())
224   result = None
225
226   errPrint("AllTestLauncher test files:\n %s" % PP.pformat(listfilesForTest))
227   
228   if len(listfilesForTest) == 0: 
229     if debug: errPrint("WARNING: AllTestLauncher: empty list of test files")
230     return None
231
232   loader = unittest.TestLoader()
233   suite = None
234
235   for i,k in enumerate(listfilesForTest):
236     if debug: errPrint("Test: %s %s" % (i, k))
237     if i == 0:
238       suite = loader.loadTestsFromModule( filesForTest[k][1] )
239       pass
240     else:
241       suite.addTests( loader.loadTestsFromModule( filesForTest[k][1] ) )
242       pass
243
244   if args.type == "std": 
245     runner = unittest.TextTestRunner(verbosity=args.verbosity)
246   elif args.type == "html": 
247     runner = HTST.HTMLTestRunner(verbosity=args.verbosity, )
248   elif args.type == "xml": 
249     if args.name == 'stdout':
250       #all-in-one xml output at 'sys.stdout' for pipe redirection
251       runner = XTST.XMLTestRunner(verbosity=args.verbosity, output=sys.stdout)
252     else:
253       #one file xml per test in suite in args.name directory
254       runner = XTST.XMLTestRunner(verbosity=args.verbosity, output=args.name)
255   else:
256     errPrint("ERROR: unknown type of output: '%s'" % args.type)
257     return None    
258     
259   if suite != None: result = runner.run(suite)
260   return result
261
262 ###################################################################
263 def runFromEnvVar(envVar, fileTestPattern="*Test.py"):
264   """
265   example: 
266     import AllTestLauncher as ATL
267     ATL.runFromEnvVar("MICROGEN_ROOT_DIR")
268     ATL.runFromEnvVar("MICROGEN_ROOT_DIR", "aggregate_*GJKTest.py")
269   """
270   env=os.environ
271   res = []
272   for i in sorted(env):
273     if envVar in i:
274       res.append(i)
275   if len(res) > 1:
276     mess = "multiple environment variable for '%s': %s" % (envVar, str(res))
277     errPrint("ERROR: " + mess)
278     return None
279   if len(res) < 1:
280     mess = "no environment variable for '%s'" % (envVar)
281     errPrint("ERROR: " + mess)
282     return None
283   res = res[0]
284   tmp = env[res].split(":")
285   if len(tmp) > 1:
286     mess = "need only one path in environment variable '%s'" % (res)
287     errPrint("ERROR: " + mess)
288     return None  
289   run(fromFileOrPath=env[res], fileTestPattern=fileTestPattern)
290
291
292 ###################################################################
293 def getParser():
294   parser = AP.ArgumentParser(description='launch All salomeTools python tests', argument_default=None)
295
296   parser.add_argument(
297     '-d', '--debug', 
298     help='set debug mode, more verbose',
299     action='store_true',
300   )
301   parser.add_argument(
302     '-v', '--verbosity', 
303     help='set verbosity of unittests [0|1|2...]',
304     default=2,
305     metavar='int'
306   )
307   parser.add_argument(
308     '-r', '--rootPath', 
309     help="""\
310 dir name with absolute or relative path stand for root directory
311 of recursive searching unittest python files
312 """,
313    default=defaultdir,
314    metavar='dirPath'
315   )
316   parser.add_argument(
317     '-p', '--pattern', 
318     help="file pattern for unittest files ['test_*.py'|'*Test.py'...]",
319     default="test_???_*.py", # as alphabetical ordered test site
320     metavar='filePattern'
321   )
322   parser.add_argument(
323     '-t', '--type', 
324     help="type of output: ['std'(standart ascii)|'xml'|'html']",
325     default="std",
326     choices=['std', 'xml', 'html'],
327     metavar='outputType'
328   )
329   parser.add_argument(
330     '-n', '--name', 
331     help="""\
332 (only for type xml)
333 name of directory output: ['test_reports'|...].
334 If name = 'stdout' then all-in-one xml output at 'sys.stdout'. For pipe redirection:
335 '>> AllTestLauncher.py -t xml -n stdout > tmp.xml'
336 """,
337     default="test_reports",
338     metavar='dirName'
339   )
340   return parser
341
342 #export PATH=defaultdir:${PATH}
343
344 ###################################################################
345 if __name__ == '__main__':
346   # Make the src & command package accessible from all code
347   # as export PYTHONPATH=defaultdir:${PYTHONPATH}
348   # https://docs.python.org/2/library/os.html
349   # On some platforms, including FreeBSD and Mac OS X, 
350   # setting environ may cause memory leak
351   # so use sys.path
352   # errPrint("INFO    : AllTestLauncher sys.path:\n'%s'" % PP.pformat(sys.path)
353   if defaultdir not in sys.path[0]:
354     sys.path.insert(0, defaultdir)
355     errPrint("WARNING : sys.path prepend '%s'\n" % defaultdir)
356
357   args = getParser().parse_args(sys.argv[1:])
358   debug = args.debug
359   directory = os.path.realpath(args.rootPath)
360   if debug: print("INFO: args:\n  %s" % PP.pformat(args))
361   sys.path.insert(0, directory) #supposed to be root of a package
362   
363   runOnArgs(args)
364
365