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