Scilab cli: add a "--timeout delay" argument
[scilab.git] / scilab / modules / development_tools / macros / test_run.sci
1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) 2007-2008 - INRIA - Pierre MARECHAL
3 // Copyright (C) 2009-2011 - DIGITEO - Michael Baudin
4 // Copyright (C) 2010-2012 - DIGITEO - Antoine ELIAS
5 // Copyright (C) 2011 - DIGITEO - Allan CORNET
6 //
7 // Copyright (C) 2012 - 2016 - Scilab Enterprises
8 //
9 // This file is hereby licensed under the terms of the GNU GPL v2.0,
10 // pursuant to article 5.3.4 of the CeCILL v.2.1.
11 // This file was originally licensed under the terms of the CeCILL v2.1,
12 // and continues to be available under such terms.
13 // For more information, see the COPYING file which you should have received
14 // along with this program.
15
16 // test_run  --
17 //   Launch unit tests.
18
19 function test_run_result = test_run(varargin)
20     lhs     = argn(1);
21     rhs     = argn(2);
22     test_run_result = %f;
23     // =========================================================================
24     // Print test_run help
25     // =========================================================================
26     if (rhs >= 3) & (~ isempty(grep(varargin(3),"help"))) then
27         example = test_examples();
28         printf("%s\n",example);
29         return;
30     end
31
32     status.detailled_failures     = "";
33     status.testsuites             = [];
34     status.test_count             = 0;
35     status.test_passed_count      = 0;
36     status.test_failed_count      = 0;
37     status.test_skipped_count     = 0;
38     status.totalTime              = 0;
39     status.list                   = [];
40
41     params.longtime               = %f;
42     params.wanted_mode            = "";
43     params.error_output           = "check";
44     params.reference              = "check";
45     params.testTypes              = "all_tests";
46     params.skip_mat               = [];
47     params.tests_mat              = [];
48     params.test_passed_percent    = 0;
49     params.test_failed_percent    = 0;
50     params.test_skipped_percent   = 0;
51     params.full_summary           = %t;
52     params.show_diff              = %f;
53     params.show_error             = %f;
54
55     // =======================================================
56     // Management of the kind of tests to run and the options
57     // =======================================================
58     if rhs >= 3 then
59
60         option_mat = varargin(3);
61         if (option_mat == "[]")
62             option_mat = [];
63         end
64         if (check_option(option_mat, "unit_tests") & check_option(option_mat, "nonreg_tests")) | check_option(option_mat, "all_tests") then
65             params.testTypes = "all_tests";
66         elseif check_option(option_mat, "unit_tests") then
67             params.testTypes = "unit_tests";
68         elseif check_option(option_mat, "nonreg_tests") then
69             params.testTypes = "nonreg_tests";
70         end
71
72         option_mat = clean_option(option_mat, "unit_tests");
73         option_mat = clean_option(option_mat, "nonreg_tests");
74         option_mat = clean_option(option_mat, "all_tests");
75
76         // Skip tests
77         params.skip_mat     = assign_option(option_mat, "skip_tests", varargin(2), params.skip_mat);
78         option_mat          = clean_option(option_mat, "skip_tests");
79
80         // Mode
81         params.wanted_mode  = assign_option(option_mat, "mode_nw", "NW", params.wanted_mode);
82         option_mat          = clean_option(option_mat, "mode_nw");
83
84         params.wanted_mode  = assign_option(option_mat, "mode_nwni", "NWNI", params.wanted_mode);
85         option_mat          = clean_option(option_mat, "mode_nwni");
86
87         params.wanted_mode  = assign_option(option_mat, "mode_nwni_profiling", ["NWNI" "PROFILING"], params.wanted_mode);
88         option_mat          = clean_option(option_mat, "mode_nwni_profiling");
89
90         // Reference
91         params.reference    = assign_option(option_mat, "no_check_ref", "skip", params.reference);
92         option_mat          = clean_option(option_mat, "no_check_ref");
93
94         params.reference    = assign_option(option_mat, "create_ref", "create", params.reference);
95         option_mat          = clean_option(option_mat, "create_ref");
96
97         params.reference    = assign_option(option_mat, "list", "list", params.reference);
98         option_mat          = clean_option(option_mat, "list");
99
100         // Error Output
101         params.error_output = assign_option(option_mat, "no_check_error_output", "skip", params.error_output);
102         option_mat          = clean_option(option_mat, "no_check_error_output");
103
104         // Enable long-time tests
105         params.longtime     = assign_option(option_mat, "disable_lt", %f, params.longtime);
106         option_mat          = clean_option(option_mat, "disable_lt");
107
108         params.longtime     = assign_option(option_mat, "enable_lt", %t, params.longtime);
109         option_mat          = clean_option(option_mat, "enable_lt");
110
111         // Summary display management
112         params.full_summary = assign_option(option_mat, "short_summary", %f, params.full_summary);
113         option_mat          = clean_option(option_mat, "short_summary");
114
115         params.show_diff = assign_option(option_mat, "show_diff", %t, params.show_diff);
116         option_mat          = clean_option(option_mat, "show_diff");
117
118         params.show_error = assign_option(option_mat, "show_error", %t, params.show_error);
119         option_mat          = clean_option(option_mat, "show_error");
120
121         if option_mat <> [] then
122             printf("\nUnrecognized option(s): \n\n");
123             for i=1:size(option_mat, "*")
124                 printf("  - ""%s""\n", option_mat(i));
125             end
126
127             return;
128         end
129
130         if rhs == 4 then
131             params.exportFile = varargin(4);
132             // Doing the XML export, force the display of the error and diff
133             params.show_diff = %t;
134             params.show_error = %t;
135         end
136     end
137
138
139     if params.reference <> "list" & params.full_summary then
140         printf("   TMPDIR = %s\n",TMPDIR);
141         printf("\n");
142     end
143     // =======================================================
144     // Management of the tests to run
145     // =======================================================
146     if (rhs == 0) ..
147         | ((rhs == 1) & (varargin(1)==[] | varargin(1)=="[]")) ..
148         | (rhs >= 2 & rhs <= 4) & ( varargin(1)==[]|varargin(1)=="[]") & (varargin(2)==[]|varargin(2)=="[]") then
149
150
151         // No input argument
152         // test_run()
153         // test_run([])
154         // => Launch each test of each module
155
156         module_list = getmodules();
157         module_list = gsort(module_list,"lr","i");
158
159         test_count = size(module_list,"*");
160         for i=1:test_count
161             if params.reference <> "list" then
162                 printf("   %03d/%03d - [%s] : \n\n", i, test_count, module_list(i));
163             end
164
165             params.tests_mat    = [];
166             params.moduleName   =  module_list(i);
167
168             result              = test_module(params);
169
170             if params.reference <> "list" then
171                 status.detailled_failures   = [status.detailled_failures; result.detailled_failures];
172                 status.testsuites(size(status.testsuites,"*")+1) = result.testsuite
173                 status.test_skipped_count   = status.test_skipped_count + result.test_skipped_count;
174
175                 // Do not take in account skipped tests
176                 status.test_count           = status.test_count + result.test_count - status.test_skipped_count;
177                 status.test_passed_count    = status.test_passed_count + result.test_passed_count;
178                 status.test_failed_count    = status.test_failed_count + result.test_failed_count;
179                 status.totalTime            = status.totalTime + result.totalTime;
180                 printf("\n");
181             else
182                 status.test_count           = status.test_count + result.test_count;
183                 status.list                 = [status.list; result.list];
184             end
185         end
186
187     elseif (rhs == 1) ..
188         | ((rhs == 2) & (varargin(2)==[] || varargin(2)=="[]")) ..
189         | ((rhs == 3) & (varargin(2)==[] || varargin(2)=="[]")) ..
190         | ((rhs == 4) & (varargin(2)==[] || varargin(2)=="[]")) ..
191         | ( ~ isempty(params.skip_mat)) then
192
193         // One input argument
194         // test_run(<module_name>)
195         // test_run([<module_name_1>,<module_name_2>])
196         // varargin(1) = [<module_name_1>,<module_name_2>]
197
198         module_mat = varargin(1);
199
200         test_count = size(module_mat,"*");
201         for i = 1:test_count
202             if params.reference <> "list" then
203                 printf("   %03d/%03d - [%s] : \n\n", i, test_count, module_mat(i));
204             end
205
206             params.tests_mat    = [];
207             params.moduleName   =  module_mat(i);
208
209             result              = test_module(params);
210
211             status.test_count   = status.test_count + result.test_count;
212             if params.reference <> "list" then
213                 status.detailled_failures       = [status.detailled_failures; result.detailled_failures];
214                 status.testsuites(size(status.testsuites,"*")+1) = result.testsuite
215
216                 status.test_skipped_count   = status.test_skipped_count + result.test_skipped_count;
217                 status.test_passed_count    = status.test_passed_count + result.test_passed_count;
218                 status.test_failed_count    = status.test_failed_count + result.test_failed_count;
219                 status.totalTime            = status.totalTime + result.totalTime;
220
221                 printf("\n");
222             else
223                 status.test_count           = status.test_count + result.test_count;
224                 status.list                 = [status.list; result.list];
225             end
226         end
227         // Do not take in account skipped tests
228         status.test_count = status.test_count - status.test_skipped_count;
229     elseif or(rhs==[2 3 4]) then
230         // Two input arguments
231         // test_run(<module_name>,<test_name>)
232         // test_run(<module_name>,[<test_name_1>,<test_name_2>] )
233
234         // varargin(1) = <module_name> ==> string 1x1
235         // varargin(2) = <test_name_1> ==> mat nl x nc
236
237         params.tests_mat    = varargin(2);
238         params.moduleName   = varargin(1);
239
240         if ((or(size(params.moduleName) <> [1,1])) & (params.tests_mat <> [])) then
241             example = test_examples();
242             err   = ["" ; msprintf(gettext("%s: Wrong size for input argument."),"test_run") ; "" ; example ];
243             printf("%s\n",err);
244             return;
245         end
246
247         result = test_module(params);
248
249         if params.reference <> "list" then
250             status.totalTime            = result.totalTime;
251             status.detailled_failures   = [status.detailled_failures; result.detailled_failures];
252             status.testsuites(size(status.testsuites,"*")+1) = result.testsuite
253             status.test_skipped_count   = status.test_skipped_count + result.test_skipped_count;
254
255             // Do not take in account skipped tests
256             status.test_count           = status.test_count + result.test_count - status.test_skipped_count;
257             status.test_passed_count    = status.test_passed_count + result.test_passed_count;
258             status.test_failed_count    = status.test_failed_count + result.test_failed_count;
259         else
260             status.test_count           = status.test_count + result.test_count;
261             status.list                 = [status.list; result.list];
262         end
263     else
264         error(msprintf(gettext("%s: Wrong number of input arguments."),"test_run"));
265     end
266
267     if params.reference == "list" then
268         test_count = size(status.list, "r");
269         for i=1:test_count
270             printf("   %4.d - [%s] %s\n", i, status.list(i, 1), status.list(i,2));
271         end
272         return;
273     end
274
275     // percent computation
276     if status.test_count <> 0 then
277         test_passed_percent  = status.test_passed_count  / status.test_count * 100;
278         test_failed_percent  = status.test_failed_count  / status.test_count * 100;
279     else
280         test_passed_percent  = 0;
281         test_failed_percent  = 0;
282     end
283
284     if isfield(params, "exportFile") then
285         exportToXUnitFormat(params.exportFile, status.testsuites);
286     end
287
288     if params.full_summary then
289         printf("\n");
290         printf("   --------------------------------------------------------------------------\n");
291         printf("   Summary\n\n");
292         printf("   tests           %4d - 100 %%\n", status.test_count);
293         printf("   passed          %4d - %3d %%\n", status.test_passed_count, test_passed_percent);
294         printf("   failed          %4d - %3d %%\n", status.test_failed_count, test_failed_percent);
295         printf("   skipped         %4d\n", status.test_skipped_count);
296         printf("   length             %4.2f sec\n", status.totalTime);
297         printf("   --------------------------------------------------------------------------\n");
298
299         if isfield(params, "exportFile") then
300             printf("   Export to          %s\n", params.exportFile);
301             printf("   --------------------------------------------------------------------------\n");
302         end
303
304         if status.test_failed_count > 0 then
305             printf("   Details\n\n");
306             printf("%s\n",status.detailled_failures);
307             printf("\n");
308             printf("   --------------------------------------------------------------------------\n");
309         end
310     else
311         printf("\n");
312         printf("   --------------------------------------------------------------------------\n");
313         printf("   Tests: %4d, ", status.test_count);
314         printf("   Passed: %4d, ", status.test_passed_count);
315         printf("   Failed: %4d, ", status.test_failed_count);
316         printf("   Skipped: %4d\n", status.test_skipped_count);
317         printf("   --------------------------------------------------------------------------\n");
318     end
319
320     //   Returns %t if no error has been detected
321     //   Returns %f if any error has been detected
322     test_run_result = (status.test_failed_count == 0);
323
324 endfunction
325
326
327
328 function status = test_module(_params)
329     name = splitModule(_params.moduleName);
330
331     if with_module(name(1)) then
332         // It's a scilab internal module
333         module.path = pathconvert(SCI + "/modules/" + name(1), %F);
334     elseif or(librarieslist() == "atomslib") & atomsIsLoaded(name(1)) then
335         // It's an ATOMS module
336         module.path = pathconvert(atomsGetLoadedPath(name(1)) , %F, %T);
337     elseif isdir(name(1)) then
338         // It's an external module
339         module.path = pathconvert(name(1), %F);
340     else
341         // It's an error
342         error(sprintf(gettext("%s is not an installed module or toolbox"), name(1)));
343     end
344
345     //get tests from path
346     my_types = ["unit_tests","nonreg_tests"];
347
348     directories = [];
349     for i=1:size(my_types,"*")
350         if (_params.testTypes == "all_tests") | (_params.testTypes == my_types(i)) then
351             directory_path = module.path + "/tests/" + my_types(i);
352             for j=2:size(name,"*")
353                 directory_path = directory_path + filesep() + name(j);
354             end
355
356             if isdir(directory_path) then
357                 directories = [directories;getDirectories(directory_path + filesep())];
358             end
359         end
360     end
361
362     tests = [];
363     if( _params.tests_mat == [])
364         for i=1:size(directories, "*")
365             currentDir = directories(i);
366             tests_mat = gsort(basename(listfiles(currentDir + filesep() + "*.tst")),"lr","i");
367
368             for j = 1:size(tests_mat, "*")
369                 if or(tests_mat(j) == _params.skip_mat) == %f then
370                     tests($+1, [1,2]) = [currentDir, tests_mat(j)];
371                 end
372             end
373         end
374     else
375         //not empty tests_mat
376         for i = 1:size(_params.tests_mat, "*")
377             bFind = %f;
378             for j = 1:size(directories, "*")
379                 currentDir = directories(j);
380                 testFile = currentDir + filesep() + _params.tests_mat(i) + ".tst";
381                 if isfile(testFile) then
382                     tests($+1, [1,2]) = [currentDir, _params.tests_mat(i)];
383                     bFind = %t;
384                 end
385             end
386
387             if bFind == %f then
388                 error(sprintf(gettext("The test ""%s"" is not available from the ""%s"" module"), _params.tests_mat(i), name(1)));
389             end
390         end
391     end
392
393     //initialize counter
394     detailled_failures      = "";
395     test_count          = size(tests, "r");
396     test_passed_count   = 0;
397     test_failed_count   = 0;
398     test_skipped_count  = 0;
399
400     moduleName          = _params.moduleName;
401     // Improve the display of the module
402     if isdir(moduleName) then
403         if part(moduleName,1:length(SCI)) == SCI then
404             moduleName = "SCI" + part(moduleName,length(SCI)+1:length(moduleName));
405         elseif part(moduleName,1:length(SCIHOME)) == SCIHOME then
406             moduleName = "SCIHOME" + part(moduleName,length(SCIHOME)+1:length(moduleName));
407         end
408     end
409
410     // For the XML export
411     testsuite.name=moduleName
412     testsuite.time=0
413     testsuite.tests=0
414     testsuite.errors=0
415
416     //don't test only return list of tests.
417     if _params.reference == "list" then
418         for i = 1:test_count
419             if size(name, "*") > 1 then
420                 displayModuleName = sprintf("%s", name(1));
421                 for j=2:size(name, "*")
422                     displayModuleName = displayModuleName + sprintf("|%s", name(j));
423                 end
424             else
425                 displayModuleName = sprintf("%s", name(1));
426             end
427             tests(i,1) = displayModuleName;
428         end
429         status.list         = tests;
430         status.test_count   = test_count;
431         return;
432     end
433
434     tic();
435     for i = 1:test_count
436         printf("   %03d/%03d - ",i, test_count);
437
438         if size(name, "*") > 1 then
439             displayModuleName = sprintf("[%s", name(1));
440             for j=2:size(name, "*")
441                 displayModuleName = displayModuleName + sprintf("|%s", name(j));
442             end
443             displayModuleName = displayModuleName + sprintf("] %s", tests(i,2));
444         else
445             displayModuleName = sprintf("[%s] %s", name(1), tests(i,2));
446         end
447
448         printf("%s", displayModuleName);
449         if length(displayModuleName) >= 50 then
450             printf(" ");
451         end
452         for j = length(displayModuleName):50
453             printf(".");
454         end
455
456         elapsedTimeBefore=toc();
457         result = test_single(_params, tests(i,1), tests(i,2));
458         elapsedTimeAfter=toc();
459
460
461         testsuite.tests = testsuite.tests + 1
462
463         testsuite.testcase(i).name=tests(i,2);
464         testsuite.testcase(i).time=elapsedTimeAfter-elapsedTimeBefore;
465         testsuite.testcase(i).skipped=(result.id >= 10) & (result.id < 20);
466
467         if result.id == 0 then
468             printf("passed\n");
469             test_passed_count = test_passed_count + 1;
470         else
471             msg = sprintf(result.message);
472             printf("%s \n", msg(1));
473             for kline = 2:size(msg, "*")
474                 printf(part(" ", 1:62) + "%s \n", msg(2));
475             end
476
477             if result.id < 10 then
478                 //failed
479                 test_failed_count = test_failed_count + 1;
480                 detailled_failures = [ detailled_failures ; sprintf("   TEST : [%s] %s", _params.moduleName, tests(i,2))];
481                 detailled_failures = [ detailled_failures ; sprintf("   %s", result.message) ];
482                 detailled_failures = [ detailled_failures ; result.details ];
483                 detailled_failures = [ detailled_failures ; "" ];
484
485                 testsuite.errors = testsuite.errors + 1
486                 testsuite.testcase(i).failure.type=result.message
487                 testsuite.testcase(i).failure.content=result.details
488
489             elseif (result.id >= 10) & (result.id < 20) then
490                 // skipped
491                 test_skipped_count = test_skipped_count + 1;
492             end
493         end
494
495         if ~isempty(result.warning) then
496             warning(result.warning);
497         end
498     end
499
500     status.totalTime = toc();
501
502     testsuite.time=status.totalTime;
503
504     clearglobal TICTOC;
505     status.test_passed_count  = test_passed_count;
506     status.test_failed_count  = test_failed_count;
507     status.test_skipped_count = test_skipped_count;
508
509     // Summary
510     status.test_count     = test_count;
511     status.detailled_failures   = detailled_failures;
512     status.testsuite   = testsuite;
513 endfunction
514
515 function status = test_single(_module, _testPath, _testName)
516     //option flag
517
518     skip          = %F;
519     interactive   = %F;
520     notyetfixed   = %F;
521     longtime      = %F;
522     reopened      = %F;
523     jvm           = %T;
524     graphic       = %F;
525     mpi           = %F;
526     execMode      = "";
527     platform      = "all";
528     language      = "any";
529     //try_catch     = %T; // Scilab 5.4.0
530     try_catch     = %f; // see comment about "dia(find(dia == '')) = [];" (~line 890)
531     error_output  = "check";
532     reference     = "check";
533     xcosNeeded    = %F;
534
535     //some paths
536     tmp_tst     = pathconvert( TMPDIR + "/" + _testName + ".tst", %F);
537     tmp_dia     = pathconvert( TMPDIR + "/" + _testName + ".dia.tmp", %F);
538     tmp_res     = pathconvert( TMPDIR + "/" + _testName + ".res", %F);
539     tmp_err     = pathconvert( TMPDIR + "/" + _testName + ".err", %F);
540     path_dia    = pathconvert( TMPDIR + "/" + _testName + ".dia", %F);
541     tmp_prof    = pathconvert( TMPDIR + "/" + _testName + ".prof", %F);
542
543     path_dia_ref  = _testPath + _testName + ".dia.ref";
544     // Reference file management OS by OS
545     if getos() == "Windows" then
546         [branch info] = getversion();
547         if info(2) == "x86" then // Look for a 32bits-specific reference file
548             altreffile  = [ _testPath + _testName + ".win.dia.ref" ; _testPath + _testName + ".win32.dia.ref" ];
549         else
550             altreffile  = [ _testPath + _testName + ".win.dia.ref" ];
551         end
552     elseif getos() == "Darwin" then
553         altreffile  = [ _testPath + _testName + ".unix.dia.ref" ; _testPath + _testName + ".macosx.dia.ref" ];
554     elseif getos() == "Linux" then
555         [branch info] = getversion();
556         if info(2) == "x86" then // Look for a 32bits-specific reference file
557             altreffile  = [ _testPath + _testName + ".unix.dia.ref" ; _testPath + _testName + ".linux.dia.ref" ; _testPath + _testName + ".linux32.dia.ref" ];
558         else
559             altreffile  = [ _testPath + _testName + ".unix.dia.ref" ; _testPath + _testName + ".linux.dia.ref" ];
560         end
561     else
562         altreffile  = [ _testPath + _testName + ".unix.dia.ref" ];
563     end
564
565     for i=1:size(altreffile,"*")
566         if isfile(altreffile(i)) then
567             path_dia_ref = altreffile(i);
568         end
569     end
570
571     //output structure
572     status.id = 0;
573     status.message = "";
574     status.details = "";
575     status.warning = "";
576
577     //Reset standard globals
578     rand("seed",0);
579     rand("uniform");
580
581     //load file
582     testFile = _testPath + _testName + ".tst";
583     sciFile = mgetl(testFile);
584
585     //read options
586     if ~isempty(grep(sciFile, "<-- NOT FIXED -->")) then
587         status.id = 10;
588         status.message = "skipped: not yet fixed";
589         return;
590     end
591
592     if ~isempty(grep(sciFile, "<-- REOPENED -->")) then
593         status.id = 10;
594         status.message = "skipped: Bug reopened";
595         return;
596     end
597
598     // platform
599     if ~isempty(grep(sciFile, "<-- WINDOWS ONLY -->")) & getos() <> "Windows" then
600         status.id = 10;
601         status.message = "skipped: Windows only";
602         return;
603     end
604
605     if ~isempty(grep(sciFile, "<-- UNIX ONLY -->")) & getos() == "Windows" then
606         status.id = 10;
607         status.message = "skipped: Unix only";
608         return;
609     end
610
611     if ~isempty(grep(sciFile, "<-- LINUX ONLY -->")) & getos() <> "Linux" then
612         status.id = 10;
613         status.message = "skipped: Linux only";
614         return;
615     end
616
617     if ~isempty(grep(sciFile, "<-- MACOSX ONLY -->")) & getos() <> "Darwin" then
618         status.id = 10;
619         status.message = "skipped: MacOSX only";
620         return;
621     end
622
623     // Test execution
624     if ~isempty(grep(sciFile, "<-- INTERACTIVE TEST -->")) then
625         status.id = 10;
626         status.message = "skipped: interactive test";
627         return;
628     end
629
630     if ~isempty(grep(sciFile, "<-- LONG TIME EXECUTION -->")) & ~_module.longtime then
631         status.id = 10;
632         status.message = "skipped: Long time duration";
633         return;
634     end
635
636     if ~isempty(grep(sciFile, "<-- TEST WITH GRAPHIC -->")) then
637         if or(_module.wanted_mode == "NWNI") then
638             status.id = 10;
639             status.message = "skipped: Test with graphic";
640             return;
641         end
642
643         graphic = %T;
644         jvm = %T;
645         execMode = "NW";
646     end
647
648     if or(_module.wanted_mode == "NWNI") & isempty(grep(sciFile, "<-- CLI SHELL MODE -->")) then
649         status.id = 10;
650         status.message = "skipped: not CLI SHELL MODE test";
651         return;
652     end
653
654     if ~isempty(grep(sciFile, "<-- JVM NOT MANDATORY -->")) then
655         status.warning = _("option ""JVM NOT MANDATORY"" is deprecated, please use ""CLI SHELL MODE"" instead");
656         jvm = %F;
657         execMode = "NWNI";
658     end
659
660     if ~isempty(grep(sciFile, "<-- CLI SHELL MODE -->")) then
661         jvm = %F;
662         execMode = "NWNI";
663     end
664
665     MPITestPos=grep(sciFile, "<-- MPI TEST")
666     if ~isempty(MPITestPos) then
667         mpi_node=msscanf(sciFile(MPITestPos), "// <-- MPI TEST %d -->")
668         if mpi_node == [] then
669             // No node found ? No worries, default to 2
670             mpi_node = 2
671         end
672         mpi = %t;
673         execMode = "NWNI";
674         reference = "skip";
675     end
676     clear MPITestPos
677
678     if ~isempty(grep(sciFile, "<-- XCOS TEST -->")) then
679         if or(_module.wanted_mode == "NWNI") then
680             status.id = 10;
681             status.message = "skipped: Test with xcos";
682             return;
683         end
684         xcosNeeded = %T;
685         jvm = %T;
686     end
687
688     // Language
689     if ~isempty(grep(sciFile, "<-- FRENCH IMPOSED -->")) then
690         language = "fr_FR";
691     end
692
693
694     if ~isempty(grep(sciFile, "<-- ENGLISH IMPOSED -->")) then
695         language = "en_US";
696     end
697
698     // Test building
699     if ~isempty(grep(sciFile, "<-- NO TRY CATCH -->")) then
700         try_catch = %F;
701     end
702
703     // Test result
704     if ~isempty(grep(sciFile, "<-- NO CHECK ERROR OUTPUT -->")) then
705         error_output = "skip";
706     end
707
708     if ~isempty(grep(sciFile, "<-- NO CHECK REF -->")) then
709         reference = "skip";
710     end
711
712     //build real test file
713
714     // Do some modification in tst file
715     //replace "pause,end" by "bugmes();quit;end"
716     sciFile = strsubst(sciFile, "pause,end", "bugmes();quit;end");
717     sciFile = strsubst(sciFile, "pause, end", "bugmes();quit;end");
718     sciFile = strsubst(sciFile, "pause;end", "bugmes();quit;end");
719     sciFile = strsubst(sciFile, "pause; end", "bugmes();quit;end");
720
721     //to avoid suppression of input --> with prompts
722     sciFile = strsubst(sciFile, "--> ", "@#> ");
723     //remove halt calls
724     sciFile = strsubst(sciFile, "halt();", "");
725
726     // Build test header
727     head = [
728     "// <-- HEADER START -->";
729     "mode(3);" ;
730     "lines(28,72);";
731     "lines(0);" ;
732     "function []=bugmes(), printf(''error on test'');endfunction"
733     "function %onprompt" ;
734     "   [msg, num] = lasterror();" ;
735     "   if (num <> 0) then" ;
736     "       bugmes()" ;
737     "   end" ;
738     "   quit;" ;
739     "endfunction" ;
740     "predef(''all'');";
741     "tmpdirToPrint = msprintf(''TMPDIR1=''''%s'''';//\n'',TMPDIR);"
742     ];
743
744     if xcosNeeded then
745         head = [
746         head;
747         "prot=funcprot(); funcprot(0);";
748         "loadXcosLibs(); loadScicos();";
749         "funcprot(prot);";
750         ];
751     end
752
753     if try_catch then
754         head = [ head ; "try" ];
755     end
756
757     head = [
758     head;
759     "diary(''" + tmp_dia + "'');";
760     "printf(''%s\n'',tmpdirToPrint);";
761     "// <-- HEADER END -->"
762     ];
763
764     // Build test footer
765     tail = [ "// <-- FOOTER START -->" ];
766
767     if try_catch then
768         tail = [
769         tail;
770         "catch";
771         "   errmsg = ""<--""+""Error on the test script file""+""-->"";";
772         "   printf(""%s\n"",errmsg);";
773         "   lasterror()";
774         "end";
775         ];
776     end
777
778     tail = [ tail; "diary(0);" ];
779
780     if graphic then
781         tail = [ tail; "xdel(winsid());sleep(1000);" ];
782     end
783
784     tail = [ tail; "exit(0);" ; "// <-- FOOTER END -->" ];
785
786     //Build final test
787     sciFile = [head ; sciFile ; tail];
788
789
790     //Build command to execute
791
792     //scilab path
793     if (getos() <> "Windows") & ~isfile(SCI+"/bin/scilab") then
794         SCI_BIN = strsubst(SCI,"share/scilab","");
795     else
796         SCI_BIN = SCI;
797     end
798
799     //mode
800     valgrind_opt = "";
801     winbin = "wscilex.exe";
802     if _module.wanted_mode == "NW" then
803         mode_arg = "-nw";
804     elseif _module.wanted_mode == "NWNI" then
805         winbin = "scilex.exe";
806         mode_arg = "-nwni";
807     elseif _module.wanted_mode == ["NWNI" "PROFILING"] && getos() == "Linux" then
808         mode_arg = "-nwni -profiling";
809         valgrind_opt = "SCILAB_VALGRIND_OPT=""--log-file=" + tmp_prof + " """;
810     else
811         if execMode == "NWNI" then
812             winbin = "scilex.exe";
813             mode_arg = "-nwni";
814         elseif execMode == "NW" then
815             mode_arg = "-nw";
816         else
817             mode_arg = "-nw";
818         end
819     end
820
821     if mpi == %t then
822         prefix_bin="mpirun -c " + string(mpi_node) + "  -bynode"
823     else
824         prefix_bin=""
825     end
826
827
828     //language
829     if language == "any" then
830         language_arg = "";
831     else
832         language_arg = "-l "+ language;
833     end
834
835     loader_path = pathconvert(fullfile(_module.moduleName, "loader.sce"), %f);
836
837     SCI_ARGS = " -nb -quit "
838     if ~_module.longtime then
839         SCI_ARGS = SCI_ARGS + "--timeout 5m "
840     end
841
842     // Build final command
843     if getos() == "Windows" then
844         if (isdir(_module.moduleName) & isfile(loader_path)) // external module not in Scilab
845             test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(""""" + loader_path + """"");exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
846         else // standard module
847             test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
848         end
849     else
850         if (isdir(_module.moduleName) & isfile(loader_path))
851             test_cmd = "( " + valgrind_opt + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + SCI_ARGS + "-e ""exec(''" + loader_path + "'');exec(''" + tmp_tst +"'');""" + " > " + tmp_res + " ) 2> " + tmp_err;
852         else
853             test_cmd = "( " + valgrind_opt + " " + prefix_bin + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + SCI_ARGS + " -f " + tmp_tst + " > " + tmp_res + " ) 2> " + tmp_err;
854         end
855     end
856
857     //clean previous tmp files
858     if isfile(tmp_tst) then
859         deletefile(tmp_tst);
860     end
861
862     if isfile(tmp_dia) then
863         deletefile(tmp_dia);
864     end
865
866     if isfile(tmp_res) then
867         deletefile(tmp_res);
868     end
869
870     if isfile(tmp_err) then
871         deletefile(tmp_err);
872     end
873
874     //create tmp test file
875     mputl(sciFile, tmp_tst);
876
877     //execute test
878     returnStatus = host(test_cmd);
879     //Check return status
880     if (returnStatus <> 0)
881         status.id = 5;
882         status.message = "failed: Slave Scilab exited with error code " + string(returnStatus);
883         return;
884     end
885
886     //Check errors
887     if (error_output == "check") & (_module.error_output == "check") then
888         if getos() == "Darwin" then
889             tmp_errfile_info = fileinfo(tmp_err);
890             msg = "Picked up _JAVA_OPTIONS:"; // When -Djava.awt.headless=false is forced for example
891
892             if ~isempty(tmp_errfile_info) then
893                 txt = mgetl(tmp_err);
894                 toRemove = grep(txt, msg);
895                 txt(toRemove) = [];
896                 if isempty(txt) then
897                     deletefile(tmp_err);
898                 else // Remove messages due to JOGL2 RC8
899                     toRemove = grep(txt, "__NSAutoreleaseNoPool()");
900                     txt(toRemove) = [];
901                     if isempty(txt) then
902                         deletefile(tmp_err);
903                     end
904                 end
905             end
906         end
907
908         if getos() == "Linux" then // Ignore JOGL2 debug message
909             tmp_errfile_info = fileinfo(tmp_err);
910             msg = "Error: unable to open display (null)"
911
912             if ~isempty(tmp_errfile_info) then
913                 txt = mgetl(tmp_err);
914                 txt(txt==msg) = [];
915
916                 // Remove messages due to warning message from external
917                 // libraries
918
919                 if ~isempty(txt) then
920                     toRemove = grep(txt, "libEGL warning: failed to find any driver");
921                     txt(toRemove) = [];
922                 end
923
924                 if ~isempty(txt) then
925                     toRemove = grep(txt, "extension ""RANDR"" missing on display");
926                     txt(toRemove) = [];
927                 end
928
929                 if ~isempty(txt) then
930                     toRemove = grep(txt, "libEGL warning: DRI2: failed to authenticate");
931                     txt(toRemove) = [];
932                 end
933
934                 if isempty(txt) then
935                     deletefile(tmp_err);
936                 end
937             end
938         end
939
940         if getos() == "Windows" then // Ignore JOGL 2.2.4 debug message
941             tmp_errfile_info = fileinfo(tmp_err);
942             msg = "Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x500";
943
944             if ~isempty(tmp_errfile_info) then
945                 txt = mgetl(tmp_err);
946                 txt(txt==msg) = [];
947                 if isempty(txt) then
948                     deletefile(tmp_err);
949                 else // Ignore JOGL 2.1.4 debug message
950                     msg = "Info: GLDrawableHelper.reshape: pre-exisiting GL error 0x500";
951                     txt(txt==msg) = [];
952                     if isempty(txt) then
953                         deletefile(tmp_err);
954                     end
955                 end
956             end
957         end
958
959         if isfile(tmp_prof) then
960             txt = mgetl(tmp_prof);
961             if grep(txt($), "ERROR SUMMARY: 0 errors from 0 contexts") then
962                 deletefile(tmp_prof);
963             else
964                 status.id = 5;
965                 status.message = "failed: Valgrind error detected";
966                 status.details = checkthefile(tmp_prof);
967                 return;
968             end
969         end
970
971
972         tmp_errfile_info = fileinfo(tmp_err);
973
974         if isfile(tmp_err) & tmp_errfile_info(1) <> 0 then
975             status.id = 5;
976             status.message = "failed: error_output not empty\n   Use ''no_check_error_output'' option to disable this check.";
977             status.details = checkthefile(tmp_err);
978             return;
979         end
980     end
981
982     //Process output files
983
984     //Get the dia file
985     if isfile(tmp_dia) then
986         dia = mgetl(tmp_dia);
987     else
988         status.id = 6;
989         status.message = "failed: Cannot find the dia file: " + tmp_dia + "\nCheck if the Scilab used correctly starts";
990         status.details = checkthefile(tmp_dia);
991         return;
992     end
993
994     // To get TMPDIR value
995     tmpdir1_line = grep(dia, "TMPDIR1");
996     execstr(dia(tmpdir1_line));
997
998     //Check for execution errors
999     if try_catch & grep(dia,"<--Error on the test script file-->") <> [] then
1000         details = [ checkthefile(tmp_dia); ..
1001         launchthecommand(testFile)];
1002         status.id = 3;
1003         status.message = "failed: premature end of the test script";
1004         status.details = details;
1005         if params.show_error == %t then
1006             status.details = [ status.details; dia($-10:$) ]
1007         end
1008
1009         return;
1010     end
1011
1012     // Remove Header and Footer
1013     dia = remove_headers(dia);
1014
1015     // Remove empty lines
1016     // In scilab 5, the test is executed in a try/catch
1017     // which remove empty lines.
1018     // In scilab 6, we can't execute the test in a try/catch
1019     // because it will be parsed first then executed
1020     // so the diary will contain all the script followed by the display
1021     // of the execution.
1022     // The try/catch is desactived ~line 513 by "try_catch     = %f;"
1023     // and the following line remove empty lines to reproduce the old operation.
1024     dia(find(dia == "")) = [];
1025
1026     //Check for execution errors
1027     dia_tmp = dia;
1028
1029     // remove commented lines
1030     dia_tmp(grep(dia_tmp, "//")) = [];
1031
1032     if try_catch & grep(dia_tmp, "!--error") <> [] then
1033         details = [ checkthefile(tmp_dia); ..
1034         launchthecommand(testFile) ];
1035         status.id = 1;
1036         status.message = "failed: the string (!--error) has been detected";
1037         status.details = details;
1038         return;
1039     end
1040
1041
1042     if grep(dia_tmp,"error on test")<>[] then
1043         details = [ checkthefile(tmp_dia); ..
1044         launchthecommand(testFile) ];
1045         status.id = 2;
1046         status.message = "failed: one or several tests failed";
1047         status.details = details;
1048         if params.show_error == %t then
1049             status.details = [ status.details; dia($-min(10, size(dia, "*")-1):$) ]
1050         end
1051         return;
1052     end
1053
1054
1055     if tmpdir1_line == [] then
1056         status.id = 6;
1057         status.message = "failed: the dia file is not correct";
1058         status.details = checkthefile(tmp_dia);
1059         return;
1060     end
1061
1062
1063     // Check the reference file only if check_ref (i.e. for the whole
1064     // test sequence) is true and this_check_ref (i.e. for the specific current .tst)
1065     // is true.
1066
1067     if (reference=="check") & (_module.reference=="check")  then
1068         if isfile(path_dia_ref) == %f then
1069             status.id = 5;
1070             status.message = "failed: the ref file doesn''t exist\n   Use ''no_check_ref'' option to disable this check.";
1071             status.details = createthefile(path_dia_ref);
1072             return;
1073         end
1074     end
1075
1076     // Comparaison ref <--> dia
1077
1078     if ( (reference=="check") & (_module.reference=="check") ) | (_module.reference=="create") then
1079         //  Do some modification in  dia file
1080
1081         dia(grep(dia, "printf(''%s\n'',tmpdirToPrint);")) = [];
1082         dia(grep(dia, "TMPDIR1")) = [];
1083         dia(grep(dia, "diary(0)")) = [];
1084
1085         if getos() == "Darwin" then // TMPDIR is a symblic link
1086             dia = strsubst(dia,"/private" + TMPDIR1, "TMPDIR");
1087             dia = strsubst(dia,"/private" + TMPDIR, "TMPDIR");
1088         end
1089         dia = strsubst(dia,TMPDIR ,"TMPDIR");
1090         dia = strsubst(dia,TMPDIR1, "TMPDIR");
1091
1092         if getos() == "Windows" then
1093             dia = strsubst(dia, strsubst(TMPDIR, "\","/"), "TMPDIR");
1094             dia = strsubst(dia, strsubst(TMPDIR1, "\","/"), "TMPDIR");
1095             dia = strsubst(dia, strsubst(TMPDIR, "/","\"), "TMPDIR");
1096             dia = strsubst(dia, strsubst(TMPDIR1, "/","\"), "TMPDIR");
1097             dia = strsubst(dia, strsubst(getshortpathname(TMPDIR), "\","/"), "TMPDIR");
1098             dia = strsubst(dia, strsubst(getshortpathname(TMPDIR1), "\","/"), "TMPDIR");
1099             dia = strsubst(dia, getshortpathname(TMPDIR), "TMPDIR");
1100             dia = strsubst(dia, getshortpathname(TMPDIR1), "TMPDIR");
1101         end
1102
1103         dia = strsubst(dia, SCI, "SCI");
1104
1105         if getos() == "Windows" then
1106             dia = strsubst(dia, strsubst(SCI, "\","/"), "SCI");
1107             dia = strsubst(dia, strsubst(SCI, "/","\"), "SCI");
1108             dia = strsubst(dia, strsubst(getshortpathname(SCI), "\","/"), "SCI");
1109             dia = strsubst(dia, getshortpathname(SCI), "SCI");
1110         end
1111
1112         //suppress the prompts
1113         dia = strsubst(dia, "--> ", "");
1114         dia = strsubst(dia, "@#> ", "--> ");
1115         dia = strsubst(dia, "-1-> ", "");
1116
1117         //standardise  number display
1118
1119         // strsubst(dia, " .", "0.");
1120         // strsubst(dia, "-.", "-0.")
1121         // strsubst(dia, "E+", "D+");
1122         // strsubst(dia, "E-", "D-");
1123
1124         //not to change the ref files
1125         dia = strsubst(dia ,"bugmes();return", "bugmes();quit");
1126
1127         if _module.reference=="create" then
1128             // Delete previous .dia.ref file
1129             if isfile(path_dia_ref) then
1130                 deletefile(path_dia_ref)
1131             end
1132
1133             mputl(dia, path_dia_ref);
1134             status.id = 20;
1135             status.message = "passed: ref created";
1136             return;
1137         else
1138             // write down the resulting dia file
1139             mputl(dia, path_dia);
1140
1141             //Check for diff with the .ref file
1142             [u,ierr] = mopen(path_dia_ref, "r");
1143             if ierr== 0 then //ref file exists
1144                 ref=mgetl(u);
1145                 mclose(u)
1146
1147                 // suppress blank (diff -nw)
1148                 dia = strsubst(dia, " ", "")
1149                 ref = strsubst(ref, " ", "")
1150
1151                 dia(find(dia == "")) = [];
1152                 ref(find(ref == "")) = [];
1153
1154                 dia(find(dia == "")) = [];
1155                 ref(find(ref == "")) = [];
1156
1157                 dia(find(part(dia, (1:2)) == "//")) = [];
1158                 ref(find(part(ref, (1:2)) == "//")) = [];
1159
1160                 if or(ref <> dia) then
1161                     status.id = 4;
1162                     status.message = "failed: dia and ref are not equal";
1163                     status.details = comparethefiles(path_dia, path_dia_ref);
1164                     return;
1165                 end
1166
1167             else
1168                 error(sprintf(gettext("The ref file (%s) doesn''t exist"), path_dia_ref));
1169             end
1170         end
1171     end
1172 endfunction
1173
1174 // checkthefile
1175 function msg = checkthefile( filename )
1176     // Returns a 2-by-1 matrix of strings, containing a message such as:
1177     //   Check the following file :
1178     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.tst
1179     // Workaround for bug #4827
1180     msg(1) = "   Check the following file :"
1181     msg(2) = "   - "+filename
1182     if params.show_error == %t then
1183         msg=[msg; mgetl(filename)]
1184     end
1185
1186 endfunction
1187
1188 // launchthecommand
1189 function msg = launchthecommand( filename )
1190     // Returns a 2-by-1 matrix of strings, containing a message such as:
1191     //   Or launch the following command :
1192     //   - exec("C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.tst")
1193     // Workaround for bug #4827
1194     msg(1) = "   Or launch the following command :"
1195     msg(2) = "   - exec(""" + fullpath(filename) + """);"
1196 endfunction
1197
1198 // => remove header from the diary txt
1199 function dia_out = remove_headers(dia_in)
1200     dia_out = dia_in;
1201     body_start = grep(dia_out,"// <-- HEADER END -->");
1202     if body_start <> [] then
1203         dia_out(1:body_start(1)) = [];
1204     end
1205
1206     body_end   = grep(dia_out,"// <-- FOOTER START -->");
1207     if body_end <> [] then
1208         [dia_nl,dia_nc] = size(dia);
1209         dia_out(body_end(1):dia_nl) = [];
1210     end
1211 endfunction
1212
1213 //createthefile
1214 function msg = createthefile ( filename )
1215     // Returns a 2-by-1 matrix of strings, containing a message such as:
1216     //   Add or create the following file :
1217     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia.ref
1218     // Workaround for bug #4827
1219     msg(1) = "   Add or create the following file: "
1220     msg(2) = "   - "+filename
1221 endfunction
1222
1223 // comparethefiles
1224 function msg = comparethefiles ( filename1 , filename2 )
1225     // Returns a 3-by-1 matrix of strings, containing a message such as:
1226     //   Compare the following files :
1227     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia
1228     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia.ref
1229     // Workaround for bug #4827
1230     msg(1) = "   Compare the following files:"
1231     msg(2) = "   - "+filename1
1232     msg(3) = "   - "+filename2
1233     if params.show_diff == %t then
1234         if getos() == "Windows" then
1235             diffTool = SCI + "\tools\diff\diff.exe";
1236         else
1237             diffTool = "diff";
1238         end
1239         targetFile=TMPDIR + filesep() + "tempdiff.diff";
1240         unix(diffTool + " -u " + filename1 + " " + filename2 + " > " + targetFile);
1241         // unix_g is failing to return the output into a variable
1242         msg=[msg; mgetl(targetFile)]
1243         deletefile(targetFile);
1244     end
1245 endfunction
1246
1247 function directories = getDirectories(directory)
1248     directories = directory;
1249     items = gsort(listfiles(directory),"lr","i");
1250
1251     for i=1:size(items,"*")
1252         if isdir(directory + items(i)) then
1253             directories = [directories; getDirectories(directory + items(i) + filesep())];
1254         end
1255     end
1256 endfunction
1257
1258 function name = splitModule(name)
1259     if ~isempty( regexp(stripblanks(name),"/\|/") ) then
1260         name = stripblanks( strsubst( strsplit(name,regexp(stripblanks(name),"/\|/")) , "/\|$/","","r" ) );
1261     end
1262 endfunction
1263
1264 function example = test_examples()
1265     example = [ sprintf("Examples :\n\n") ];
1266     example = [ example ; sprintf("// Launch all tests\n") ];
1267     example = [ example ; sprintf("test_run();\n") ];
1268     example = [ example ; sprintf("test_run([]);\n") ];
1269     example = [ example ; sprintf("test_run([],[]);\n") ];
1270     example = [ example ; "" ];
1271     example = [ example ; sprintf("// Test one or several module\n") ];
1272     example = [ example ; sprintf("test_run(''core'');\n") ];
1273     example = [ example ; sprintf("test_run(''core'',[]);\n") ];
1274     example = [ example ; sprintf("test_run([''core'',''string'']);\n") ];
1275     example = [ example ; "" ];
1276     example = [ example ; sprintf("// Launch one or several test in a specified module\n") ];
1277     example = [ example ; sprintf("test_run(''core'',[''trycatch'',''opcode'']);\n") ];
1278     example = [ example ; "" ];
1279     example = [ example ; sprintf("// With options\n") ];
1280     example = [ example ; sprintf("test_run([],[],''no_check_ref'');\n") ];
1281     example = [ example ; sprintf("test_run([],[],''no_check_error_output'');\n") ];
1282     example = [ example ; sprintf("test_run([],[],''create_ref'');\n") ];
1283     example = [ example ; sprintf("test_run([],[],''list'');\n") ];
1284     example = [ example ; sprintf("test_run([],[],''help'');\n") ];
1285     example = [ example ; sprintf("test_run([],[],[''no_check_ref'',''mode_nw'']);\n") ];
1286     example = [ example ; "" ];
1287 endfunction
1288
1289 function newOption = clean_option(var, option)
1290     newOption = var;
1291     newOption(newOption == option) = [];
1292 endfunction
1293
1294 function result = check_option(var, option)
1295     result = or(var == option);
1296 endfunction
1297
1298 function value = assign_option(var, option, truevalue, falsevalue)
1299     if check_option(var, option) then
1300         value = truevalue;
1301     else
1302         value = falsevalue;
1303     end
1304 endfunction
1305
1306
1307 function exportToXUnitFormat(exportToFile, testsuites)
1308
1309     if isfile(exportToFile) then
1310         // File already existing. Append the results
1311         doc = xmlRead(exportToFile);
1312         appendIntoFile = %t;
1313         node = xmlXPath(doc, "//testsuites");
1314         if node.size == 0 then
1315             error(msprintf(gettext("The file ''%s'' is not following the XUnit XML format. Root tag expected ''testsuites''.\n"),exportToFile))
1316         end
1317     else
1318         doc = xmlDocument(exportToFile);
1319
1320         appendIntoFile = %f;
1321     end
1322     root = xmlElement(doc, "testsuites");
1323
1324     for i=1:size(testsuites, "*") // Export module by module
1325         module = testsuites(i);
1326         testsuite = xmlElement(doc,"testsuite");
1327         testsuite.attributes.name = module.name;
1328
1329         testsuite.attributes.time  = string(module.time);
1330
1331         testsuite.attributes.tests = string(module.tests);
1332         testsuite.attributes.errors = string(module.errors);
1333
1334         for j=1:size(module.testcase,"*") // Export test by test
1335             testsuite.children(j) = xmlElement(doc,"testcase");
1336             unitTest = module.testcase(j);
1337             testsuite.children(j).attributes.name = unitTest.name;
1338             testsuite.children(j).attributes.time = string(unitTest.time);
1339             testsuite.children(j).attributes.classname = getversion()+"."+module.name;
1340             if isfield(unitTest,"failure") & size(unitTest.failure,"*") >= 1 then
1341                 testsuite.children(j).children(1) = xmlElement(doc,"failure");
1342                 testsuite.children(j).children(1).attributes.type = unitTest.failure.type;
1343                 content = unitTest.failure.content;
1344                 for kL=1:size(content, "*")
1345                     ampIdx = strindex(content(kL), "&");
1346                     while ~isempty(ampIdx)
1347                         cur = ampIdx(1);
1348                         ampIdx(1) = [];
1349                         if or(part(content(kL), (cur+1):(cur+3))==["gt;" "lt"]) then
1350                             // Ignored
1351                         else
1352                             content(kL) = part(content(kL), 1:cur) + "amp;" + part(content(kL), (cur+1):$);
1353                             ampIdx = strindex(part(content(kL), (cur+1):$), "&");
1354                         end
1355                     end
1356                 end
1357                 testsuite.children(j).children(1).content = content;
1358             elseif unitTest.skipped then
1359                 testsuite.children(j).children(1) = xmlElement(doc,"skipped");
1360             end
1361         end
1362
1363         if appendIntoFile then
1364             // We will add the new elements into 'testsuites'
1365             c=node(1).children;
1366             nb=size(c,"*");
1367             c(nb + 1)=testsuite; // Add the new results into the list of results
1368             root.children=c;
1369         else
1370             root.children(i)=testsuite
1371         end
1372     end // list of modules
1373
1374     doc.root=root
1375
1376     xmlWrite(doc);
1377 endfunction