Salome HOME
begin fix sat test
[tools/sat.git] / commands / test.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2012  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import sys
21 import shutil
22 import subprocess
23 import datetime
24 import gzip
25
26 verbose = False
27
28 try:
29     from hashlib import sha1
30 except ImportError:
31     from sha import sha as sha1
32
33 import src
34 import src.ElementTree as etree
35 from src.xmlManager import add_simple_node
36
37 # Define all possible option for the test command :  sat test <options>
38 parser = src.options.Options()
39 parser.add_option('b', 'base', 'string', 'base',
40     _("Optional: Indicate the name of the test base to use.\n\tThis name has to"
41       " be registered in your application and in a project.\n\tA path to a "
42       "test base can also be used."))
43 parser.add_option('l', 'launcher', 'string', 'launcher',
44     _("Optional: Use this option to specify the path to a SALOME launcher to "
45       "use to launch the test scripts of the test base."))
46 parser.add_option('g', 'grid', 'list', 'grids',
47     _('Optional: Indicate which grid(s) to test (subdirectory of the test '
48       'base).'))
49 parser.add_option('s', 'session', 'list2', 'sessions',
50     _('Optional: indicate which session(s) to test (subdirectory of the '
51       'grid).'))
52 parser.add_option('', 'display', 'string', 'display',
53     _("Optional: set the display where to launch SALOME.\n"
54 "\tIf value is NO then option --show-desktop=0 will be used to launch SALOME."))
55 parser.add_option('', 'keep', 'boolean', 'keeptempdir',
56                   _('Optional: keep temporary directory.'))
57 def description():
58     '''method that is called when salomeTools is called with --help option.
59     
60     :return: The text to display for the test command description.
61     :rtype: str
62     '''
63     return _("The test command runs a test base on a SALOME installation.\n\n"
64              "example:\nsat test SALOME-master --grid GEOM --session light")     
65
66 def parse_option_old(args, config):
67     """ Parse the options and do some verifications about it
68     
69     :param args List: The list of arguments of the command
70     :param config Config: The global configuration
71     :return: the options of the current command launch and the full arguments
72     :rtype: Tuple (options, args)
73     """
74     (options, args) = parser.parse_args(args)
75
76     if not options.launcher:
77         options.launcher = ""
78     elif not os.path.isabs(options.launcher):
79         if not src.config_has_application(config):
80             msg = _("An application is required to use a relative path with option --appli")
81             raise src.SatException(msg)
82         options.launcher = os.path.join(config.APPLICATION.workdir,
83                                         options.launcher)
84
85         if not os.path.exists(options.launcher):
86             raise src.SatException(_("Launcher not found: %s") % 
87                                    options.launcher)
88
89     return (options, args)
90
91
92 def parse_option(args, config):
93     """ Parse the options and do some verifications about it
94
95     :param args List: The list of arguments of the command
96     :param config Config: The global configuration
97     :return: the options of the current command launch and the full arguments
98     :rtype: Tuple (options, args)
99     """
100     (options, args) = parser.parse_args(args)
101
102     if not options.launcher:
103         options.launcher = ""
104         return (options, args)
105
106     if not os.path.isabs(options.launcher):
107         if not src.config_has_application(config):
108             msg = _("An application is required to use a relative path with option --appli")
109             raise src.SatException(msg)
110         else:
111             options.launcher = os.path.join(config.APPLICATION.workdir, options.launcher)
112             if not os.path.exists(options.launcher):
113                 raise src.SatException(_("Launcher not found: %s") %  options.launcher)
114
115     # absolute path
116     launcher = os.path.realpath(os.path.expandvars(options.launcher))
117     if os.path.exists(launcher):
118         options.launcher = launcher
119         return (options, args)
120
121     raise src.SatException(_("Launcher not found: %s") %  options.launcher)
122
123
124 def ask_a_path():
125     """ 
126     """
127     path = raw_input("enter a path where to save the result: ")
128     if path == "":
129         result = raw_input("the result will be not save. Are you sure to "
130                            "continue ? [y/n] ")
131         if result == "y":
132             return path
133         else:
134             return ask_a_path()
135
136     elif os.path.exists(path):
137         result = raw_input("Warning, the content of %s will be deleted. Are you"
138                            " sure to continue ? [y/n] " % path)
139         if result == "y":
140             return path
141         else:
142             return ask_a_path()
143     else:
144         return path
145
146 def save_file(filename, base):
147     f = open(filename, 'r')
148     content = f.read()
149     f.close()
150
151     objectname = sha1(content).hexdigest()
152
153     f = gzip.open(os.path.join(base, '.objects', objectname), 'w')
154     f.write(content)
155     f.close()
156     return objectname
157
158 def move_test_results(in_dir, what, out_dir, logger):
159     if out_dir == in_dir:
160         return
161
162     finalPath = out_dir
163     pathIsOk = False
164     while not pathIsOk:
165         try:
166             # create test results directory if necessary
167             #logger.write("FINAL = %s\n" % finalPath, 5)
168             if not os.access(finalPath, os.F_OK):
169                 #shutil.rmtree(finalPath)
170                 os.makedirs(finalPath)
171             pathIsOk = True
172         except:
173             logger.error(_("%s cannot be created.") % finalPath)
174             finalPath = ask_a_path()
175
176     if finalPath != "":
177         os.makedirs(os.path.join(finalPath, what, 'BASES'))
178
179         # check if .objects directory exists
180         if not os.access(os.path.join(finalPath, '.objects'), os.F_OK):
181             os.makedirs(os.path.join(finalPath, '.objects'))
182
183         logger.write(_('copy tests results to %s ... ') % finalPath, 3)
184         logger.flush()
185         #logger.write("\n", 5)
186
187         # copy env_info.py
188         shutil.copy2(os.path.join(in_dir, what, 'env_info.py'),
189                      os.path.join(finalPath, what, 'env_info.py'))
190
191         # for all sub directory (ie testbase) in the BASES directory
192         for testbase in os.listdir(os.path.join(in_dir, what, 'BASES')):
193             outtestbase = os.path.join(finalPath, what, 'BASES', testbase)
194             intestbase = os.path.join(in_dir, what, 'BASES', testbase)
195
196             # ignore files in root dir
197             if not os.path.isdir(intestbase):
198                 continue
199
200             os.makedirs(outtestbase)
201             #logger.write("  copy testbase %s\n" % testbase, 5)
202
203             for grid_ in [m for m in os.listdir(intestbase) \
204                             if os.path.isdir(os.path.join(intestbase, m))]:
205                 # ignore source configuration directories
206                 if grid_[:4] == '.git' or grid_ == 'CVS':
207                     continue
208
209                 outgrid = os.path.join(outtestbase, grid_)
210                 ingrid = os.path.join(intestbase, grid_)
211                 os.makedirs(outgrid)
212                 #logger.write("    copy grid %s\n" % grid_, 5)
213
214                 if grid_ == 'RESSOURCES':
215                     for file_name in os.listdir(ingrid):
216                         if not os.path.isfile(os.path.join(ingrid,
217                                                            file_name)):
218                             continue
219                         f = open(os.path.join(outgrid, file_name), "w")
220                         f.write(save_file(os.path.join(ingrid, file_name),
221                                           finalPath))
222                         f.close()
223                 else:
224                     for session_name in [t for t in os.listdir(ingrid) if 
225                                       os.path.isdir(os.path.join(ingrid, t))]:
226                         outsession = os.path.join(outgrid, session_name)
227                         insession = os.path.join(ingrid, session_name)
228                         os.makedirs(outsession)
229                         
230                         for file_name in os.listdir(insession):
231                             if not os.path.isfile(os.path.join(insession,
232                                                                file_name)):
233                                 continue
234                             if file_name.endswith('result.py'):
235                                 shutil.copy2(os.path.join(insession, file_name),
236                                              os.path.join(outsession, file_name))
237                             else:
238                                 f = open(os.path.join(outsession, file_name), "w")
239                                 f.write(save_file(os.path.join(insession,
240                                                                file_name),
241                                                   finalPath))
242                                 f.close()
243
244     logger.write(src.printcolors.printc("OK"), 3, False)
245     logger.write("\n", 3, False)
246
247 def check_remote_machine(machine_name, logger):
248     logger.write(_("\ncheck the display on %s\n" % machine_name), 4)
249     ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s "ls"' % machine_name
250     logger.write(_("Executing the command : %s " % ssh_cmd), 4)
251     p = subprocess.Popen(ssh_cmd, 
252                          shell=True,
253                          stdin =subprocess.PIPE,
254                          stdout=subprocess.PIPE,
255                          stderr=subprocess.PIPE)
256     p.wait()
257     if p.returncode != 0:
258         logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 1)
259         logger.write("    " + src.printcolors.printcError(p.stderr.read()), 2)
260         logger.write(src.printcolors.printcWarning((
261                                     "No ssh access to the display machine.")),1)
262     else:
263         logger.write(src.printcolors.printcSuccess(src.OK_STATUS) + "\n\n", 4)
264
265 ##
266 # Creates the XML report for a product.
267 def create_test_report(config,
268                        xml_history_path,
269                        dest_path,
270                        retcode,
271                        xmlname=""):
272     # get the date and hour of the launching of the command, in order to keep
273     # history
274     date_hour = config.VARS.datehour
275     
276     # Get some information to put in the xml file
277     application_name = config.VARS.application
278     withappli = src.config_has_application(config)
279     
280     first_time = False
281     if not os.path.exists(xml_history_path):
282         print("Log file creation %s" % xml_history_path)
283         first_time = True
284         root = etree.Element("salome")
285         prod_node = etree.Element("product", name=application_name, build=xmlname)
286         root.append(prod_node)
287     else:
288         print("Log file modification %s" % xml_history_path)
289         root = etree.parse(xml_history_path).getroot()
290         prod_node = root.find("product")
291
292
293     prod_node.attrib["history_file"] = os.path.basename(xml_history_path)
294     prod_node.attrib["global_res"] = retcode
295
296     if withappli:
297         if not first_time:
298             for node in (prod_node.findall("version_to_download") + 
299                          prod_node.findall("out_dir")):
300                 prod_node.remove(node)
301                 
302         add_simple_node(prod_node, "version_to_download",
303                         config.APPLICATION.name)
304         
305         add_simple_node(prod_node, "out_dir", config.APPLICATION.workdir)
306
307     # add environment
308     if not first_time:
309         for node in prod_node.findall("exec"):
310                 prod_node.remove(node)
311         
312     exec_node = add_simple_node(prod_node, "exec")
313     exec_node.append(etree.Element("env", name="Host", value=config.VARS.node))
314     exec_node.append(etree.Element("env", name="Architecture",
315                                    value=config.VARS.dist))
316     exec_node.append(etree.Element("env", name="Number of processors",
317                                    value=str(config.VARS.nb_proc)))    
318     exec_node.append(etree.Element("env", name="Begin date",
319                                    value=src.parse_date(date_hour)))
320     exec_node.append(etree.Element("env", name="Command",
321                                    value=config.VARS.command))
322     exec_node.append(etree.Element("env", name="sat version",
323                                    value=config.INTERNAL.sat_version))
324
325     if 'TESTS' in config:
326         if first_time:
327             tests = add_simple_node(prod_node, "tests")
328             known_errors = add_simple_node(prod_node, "known_errors")
329             new_errors = add_simple_node(prod_node, "new_errors")
330             amend = add_simple_node(prod_node, "amend")
331         else:
332             tests = prod_node.find("tests")
333             known_errors = prod_node.find("known_errors")
334             new_errors = prod_node.find("new_errors")
335             amend = prod_node.find("amend")
336         
337         tt = {}
338         for test in config.TESTS:
339             if not test.testbase in tt:
340                 tt[test.testbase] = [test]
341             else:
342                 tt[test.testbase].append(test)
343         
344         for testbase in tt.keys():
345             if verbose: print("---- create_test_report %s %s" % (testbase, first_time))
346             if first_time:
347                 gn = add_simple_node(tests, "testbase")
348             else:
349                 gn = tests.find("testbase")
350                 # initialize all grids and session to "not executed"
351                 for mn in gn.findall("grid"):
352                     mn.attrib["executed_last_time"] = "no"
353                     for tyn in mn.findall("session"):
354                         tyn.attrib["executed_last_time"] = "no"
355                         for test_node in tyn.findall('test'):
356                             for node in test_node.getchildren():
357                                 if node.tag != "history":
358                                     test_node.remove(node)
359                             
360                             attribs_to_pop = []    
361                             for attribute in test_node.attrib:
362                                 if (attribute != "script" and 
363                                                         attribute != "res"):
364                                     attribs_to_pop.append(attribute)
365                             for attribute in attribs_to_pop:
366                                 test_node.attrib.pop(attribute)
367             
368             gn.attrib['name'] = testbase
369             nb, nb_pass, nb_failed, nb_timeout, nb_not_run = 0, 0, 0, 0, 0
370             grids = {}
371             sessions = {}
372             for test in tt[testbase]:
373                 if not grids.has_key(test.grid):
374                     if first_time:
375                         mn = add_simple_node(gn, "grid")
376                         mn.attrib['name'] = test.grid
377                     else:
378                         l_mn = gn.findall("grid")
379                         mn = None
380                         for grid_node in l_mn:
381                             if grid_node.attrib['name'] == test.grid:
382                                 mn = grid_node
383                                 break
384                         if mn == None:
385                             mn = add_simple_node(gn, "grid")
386                             mn.attrib['name'] = test.grid
387                     
388                     grids[test.grid] = mn
389                 
390                 mn.attrib["executed_last_time"] = "yes"
391                 
392                 if not "%s/%s" % (test.grid, test.session) in sessions:
393                     if first_time:
394                         tyn = add_simple_node(mn, "session")
395                         tyn.attrib['name'] = test.session
396                     else:
397                         l_tyn = mn.findall("session")
398                         tyn = None
399                         for session_node in l_tyn:
400                             if session_node.attrib['name'] == test.session:
401                                 tyn = session_node
402                                 break
403                         if tyn == None:
404                             tyn = add_simple_node(mn, "session")
405                             tyn.attrib['name'] = test.session
406                         
407                     sessions["%s/%s" % (test.grid, test.session)] = tyn
408
409                 tyn.attrib["executed_last_time"] = "yes"
410
411                 for script in test.script:
412                     if first_time:
413                         tn = add_simple_node(sessions[
414                                            "%s/%s" % (test.grid, test.session)],
415                                              "test")
416                         tn.attrib['session'] = test.session
417                         tn.attrib['script'] = script.name
418                         hn = add_simple_node(tn, "history")
419                     else:
420                         l_tn = sessions["%s/%s" % (test.grid, test.session)].findall(
421                                                                          "test")
422                         tn = None
423                         for test_node in l_tn:
424                             if test_node.attrib['script'] == script['name']:
425                                 tn = test_node
426                                 break
427                         
428                         if tn == None:
429                             tn = add_simple_node(sessions[
430                                            "%s/%s" % (test.grid, test.session)],
431                                              "test")
432                             tn.attrib['session'] = test.session
433                             tn.attrib['script'] = script.name
434                             hn = add_simple_node(tn, "history")
435                         else:
436                             # Get or create the history node for the current test
437                             if len(tn.findall("history")) == 0:
438                                 hn = add_simple_node(tn, "history")
439                             else:
440                                 hn = tn.find("history")
441                             # Put the last test data into the history
442                             if 'res' in tn.attrib:
443                                 attributes = {"date_hour" : date_hour,
444                                               "res" : tn.attrib['res'] }
445                                 add_simple_node(hn,
446                                                 "previous_test",
447                                                 attrib=attributes)
448                             for node in tn:
449                                 if node.tag != "history":
450                                     tn.remove(node)
451                     
452                     if 'callback' in script:
453                         try:
454                             cnode = add_simple_node(tn, "callback")
455                             if src.architecture.is_windows():
456                                 import string
457                                 cnode.text = filter(
458                                                 lambda x: x in string.printable,
459                                                 script.callback)
460                             else:
461                                 cnode.text = script.callback.decode(
462                                                                 'string_escape')
463                         except UnicodeDecodeError as exc:
464                             zz = (script.callback[:exc.start] +
465                                   '?' +
466                                   script.callback[exc.end-2:])
467                             cnode = add_simple_node(tn, "callback")
468                             cnode.text = zz.decode("UTF-8")
469                     
470                     # Add the script content
471                     cnode = add_simple_node(tn, "content")
472                     cnode.text = script.content
473                     
474                     # Add the script execution log
475                     cnode = add_simple_node(tn, "out")
476                     cnode.text = script.out
477                     
478                     if 'amend' in script:
479                         cnode = add_simple_node(tn, "amend")
480                         cnode.text = script.amend.decode("UTF-8")
481
482                     if script.time < 0:
483                         tn.attrib['exec_time'] = "?"
484                     else:
485                         tn.attrib['exec_time'] = "%.3f" % script.time
486                     tn.attrib['res'] = script.res
487
488                     if "amend" in script:
489                         amend_test = add_simple_node(amend, "atest")
490                         amend_test.attrib['name'] = os.path.join(test.grid,
491                                                                  test.session,
492                                                                  script.name)
493                         amend_test.attrib['reason'] = script.amend.decode(
494                                                                         "UTF-8")
495
496                     # calculate status
497                     nb += 1
498                     if script.res == src.OK_STATUS: nb_pass += 1
499                     elif script.res == src.TIMEOUT_STATUS: nb_timeout += 1
500                     elif script.res == src.KO_STATUS: nb_failed += 1
501                     else: nb_not_run += 1
502
503                     if "known_error" in script:
504                         kf_script = add_simple_node(known_errors, "error")
505                         kf_script.attrib['name'] = os.path.join(test.grid,
506                                                                 test.session,
507                                                                 script.name)
508                         kf_script.attrib['date'] = script.known_error.date
509                         kf_script.attrib[
510                                     'expected'] = script.known_error.expected
511                         kf_script.attrib[
512                          'comment'] = script.known_error.comment.decode("UTF-8")
513                         kf_script.attrib['fixed'] = str(
514                                                        script.known_error.fixed)
515                         overdue = datetime.datetime.today().strftime("%Y-%m-"
516                                             "%d") > script.known_error.expected
517                         if overdue:
518                             kf_script.attrib['overdue'] = str(overdue)
519                         
520                     elif script.res == src.KO_STATUS:
521                         new_err = add_simple_node(new_errors, "new_error")
522                         script_path = os.path.join(test.grid,
523                                                    test.session, script.name)
524                         new_err.attrib['name'] = script_path
525                         new_err.attrib['cmd'] = ("sat testerror %s -s %s -c 'my"
526                                                  " comment' -p %s" % \
527                             (application_name, script_path, config.VARS.dist))
528
529
530             gn.attrib['total'] = str(nb)
531             gn.attrib['pass'] = str(nb_pass)
532             gn.attrib['failed'] = str(nb_failed)
533             gn.attrib['timeout'] = str(nb_timeout)
534             gn.attrib['not_run'] = str(nb_not_run)
535             
536             # Remove the res attribute of all tests that were not launched 
537             # this time
538             for mn in gn.findall("grid"):
539                 if mn.attrib["executed_last_time"] == "no":
540                     for tyn in mn.findall("session"):
541                         if tyn.attrib["executed_last_time"] == "no":
542                             for test_node in tyn.findall('test'):
543                                 if "res" in test_node.attrib:
544                                     test_node.attrib.pop("res")          
545     
546     if len(xmlname) == 0:
547         xmlname = application_name
548     if not xmlname.endswith(".xml"):
549         xmlname += ".xml"
550
551     src.xmlManager.write_report(os.path.join(dest_path, xmlname), root, "test.xsl")
552     src.xmlManager.write_report(xml_history_path, root, "test_history.xsl")
553     return src.OK_STATUS
554
555 def generate_history_xml_path(config, test_base):
556     """Generate the name of the xml file that contain the history of the tests
557        on the machine with the current APPLICATION and the current test base.
558     
559     :param config Config: The global configuration
560     :param test_base Str: The test base name (or path)
561     :return: the full path of the history xml file
562     :rtype: Str
563     """
564     history_xml_name = ""
565     if "APPLICATION" in config:
566         history_xml_name += config.APPLICATION.name
567         history_xml_name += "-" 
568     history_xml_name += config.VARS.dist
569     history_xml_name += "-"
570     test_base_name = test_base
571     if os.path.exists(test_base):
572         test_base_name = os.path.basename(test_base)
573     history_xml_name += test_base_name
574     history_xml_name += ".xml"
575     log_dir = src.get_log_path(config)
576     return os.path.join(log_dir, "TEST", history_xml_name)
577
578 def run(args, runner, logger):
579     '''method that is called when salomeTools is called with test parameter.
580     '''
581     (options, args) = parse_option(args, runner.cfg)
582
583     # the test base is specified either by the application, or by the --base option
584     with_application = False
585     if runner.cfg.VARS.application != 'None':
586         logger.write(_('Running tests on application %s\n') % 
587                             src.printcolors.printcLabel(
588                                                 runner.cfg.VARS.application), 1)
589         with_application = True
590     elif not options.base:
591         raise src.SatException(_('A test base is required. Use the --base '
592                                  'option'))
593
594     # the launcher is specified either by the application, or by the --launcher option
595     if with_application:
596         # check if environment is loaded
597         if 'KERNEL_ROOT_DIR' in os.environ:
598             logger.write(src.printcolors.printcWarning(_("WARNING: "
599                             "SALOME environment already sourced")) + "\n", 1)
600             
601         
602     elif options.launcher:
603         logger.write(src.printcolors.printcWarning(_("Running SALOME "
604                                                 "application.")) + "\n\n", 1)
605     else:
606         msg = _("Impossible to find any launcher.\nPlease specify an "
607                 "application or a launcher")
608         logger.write(src.printcolors.printcError(msg))
609         logger.write("\n")
610         return 1
611
612     # set the display
613     show_desktop = (options.display and options.display.upper() == "NO")
614     if options.display and options.display != "NO":
615         remote_name = options.display.split(':')[0]
616         if remote_name != "":
617             check_remote_machine(remote_name, logger)
618         # if explicitly set use user choice
619         os.environ['DISPLAY'] = options.display
620     elif 'DISPLAY' not in os.environ:
621         # if no display set
622         if ('test' in runner.cfg.LOCAL and
623                 'display' in runner.cfg.LOCAL.test and 
624                 len(runner.cfg.LOCAL.test.display) > 0):
625             # use default value for test tool
626             os.environ['DISPLAY'] = runner.cfg.LOCAL.test.display
627         else:
628             os.environ['DISPLAY'] = "localhost:0.0"
629
630     # initialization
631     #################
632     if with_application:
633         tmp_dir = os.path.join(runner.cfg.VARS.tmp_root,
634                                runner.cfg.APPLICATION.name,
635                                "test")
636     else:
637         tmp_dir = os.path.join(runner.cfg.VARS.tmp_root,
638                                "test")
639
640     # remove previous tmp dir
641     if os.access(tmp_dir, os.F_OK):
642         try:
643             shutil.rmtree(tmp_dir)
644         except:
645             logger.error(_("error removing TT_TMP_RESULT %s\n") 
646                                 % tmp_dir)
647
648     lines = []
649     lines.append("date = '%s'" % runner.cfg.VARS.date)
650     lines.append("hour = '%s'" % runner.cfg.VARS.hour)
651     lines.append("node = '%s'" % runner.cfg.VARS.node)
652     lines.append("arch = '%s'" % runner.cfg.VARS.dist)
653
654     if 'APPLICATION' in runner.cfg:
655         lines.append("application_info = {}")
656         lines.append("application_info['name'] = '%s'" % 
657                      runner.cfg.APPLICATION.name)
658         lines.append("application_info['tag'] = '%s'" % 
659                      runner.cfg.APPLICATION.tag)
660         lines.append("application_info['products'] = %s" % 
661                      str(runner.cfg.APPLICATION.products))
662
663     content = "\n".join(lines)
664
665     # create hash from context information
666     # CVW TODO or not dirname = datetime.datetime.now().strftime("%y%m%d_%H%M%S_") + sha1(content.encode()).hexdigest()[0:8]
667     dirname = sha1(content.encode()).hexdigest()[0:8] # only 8 firsts probably good
668     base_dir = os.path.join(tmp_dir, dirname)
669     os.makedirs(base_dir)
670     os.environ['TT_TMP_RESULT'] = base_dir
671
672     # create env_info file
673     with open(os.path.join(base_dir, 'env_info.py'), "w") as f:
674         f.write(content)
675
676     # create working dir and bases dir
677     working_dir = os.path.join(base_dir, 'WORK')
678     os.makedirs(working_dir)
679     os.makedirs(os.path.join(base_dir, 'BASES'))
680     os.chdir(working_dir)
681
682     if 'PYTHONPATH' not in os.environ:
683         os.environ['PYTHONPATH'] = ''
684     else:
685         for var in os.environ['PYTHONPATH'].split(':'):
686             if var not in sys.path:
687                 sys.path.append(var)
688
689     # launch of the tests
690     #####################
691     test_base = ""
692     if options.base:
693         test_base = options.base
694     elif with_application and "test_base" in runner.cfg.APPLICATION:
695         test_base = runner.cfg.APPLICATION.test_base.name
696
697     src.printcolors.print_value(logger, _('Display'), os.environ['DISPLAY'], 2)
698     src.printcolors.print_value(logger, _('Timeout'),
699                                 src.test_module.DEFAULT_TIMEOUT, 2)
700     src.printcolors.print_value(logger, _("Working dir"), base_dir, 3)
701
702     # create the test object
703     test_runner = src.test_module.Test(runner.cfg,
704                                   logger,
705                                   base_dir,
706                                   testbase=test_base,
707                                   grids=options.grids,
708                                   sessions=options.sessions,
709                                   launcher=options.launcher,
710                                   show_desktop=show_desktop)
711     
712     if not test_runner.test_base_found:
713         # Fail 
714         return 1
715         
716     # run the test
717     logger.allowPrintLevel = False
718     retcode = test_runner.run_all_tests()
719     logger.allowPrintLevel = True
720
721     logger.write(_("Tests finished"), 1)
722     logger.write("\n", 2, False)
723     
724     logger.write(_("\nGenerate the specific test log\n"), 5)
725     log_dir = src.get_log_path(runner.cfg)
726     out_dir = os.path.join(log_dir, "TEST")
727     src.ensure_path_exists(out_dir)
728     name_xml_board = logger.logFileName.split(".")[0] + "_board.xml"
729     historic_xml_path = generate_history_xml_path(runner.cfg, test_base)
730     
731     create_test_report(runner.cfg,
732                        historic_xml_path,
733                        out_dir,
734                        retcode,
735                        xmlname = name_xml_board)
736     xml_board_path = os.path.join(out_dir, name_xml_board)
737
738     logger.l_logFiles.append(xml_board_path)
739     logger.add_link(os.path.join("TEST", name_xml_board),
740                     "board",
741                     retcode,
742                     "Click on the link to get the detailed test results")
743     logger.write("\nTests board file %s\n" % xml_board_path, 1)
744
745     # Add the historic files into the log files list of the command
746     logger.l_logFiles.append(historic_xml_path)
747
748     if not options.keeptempdir:
749       logger.write("Removing the temporary directory: rm -rf %s\n" % test_runner.tmp_working_dir, 5)
750       if os.path.exists(test_runner.tmp_working_dir):
751         shutil.rmtree(test_runner.tmp_working_dir)
752     else:
753       logger.write("NOT Removing the temporary directory: rm -rf %s\n" % test_runner.tmp_working_dir, 5)
754
755     return retcode
756