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