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