a48518e9ea769999e94f6df36c7575d4dbfdad35
[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     // Build final command
838     if getos() == "Windows" then
839         if (isdir(_module.moduleName) & isfile(loader_path)) // external module not in Scilab
840             test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(""""" + loader_path + """"");exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
841         else // standard module
842             test_cmd = "( """ + SCI_BIN + "\bin\" + winbin + """" + " " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(""""" + tmp_tst + """"", -1);"" > """ + tmp_res + """ ) 2> """ + tmp_err + """";
843         end
844     else
845         if (isdir(_module.moduleName) & isfile(loader_path))
846             test_cmd = "( " + valgrind_opt + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + " -nb -quit -e ""exec(''" + loader_path + "'');exec(''" + tmp_tst +"'');""" + " > " + tmp_res + " ) 2> " + tmp_err;
847         else
848             test_cmd = "( " + valgrind_opt + " " + prefix_bin + " " + SCI_BIN + "/bin/scilab " + mode_arg + " " + language_arg + " -nb -quit -f " + tmp_tst + " > " + tmp_res + " ) 2> " + tmp_err;
849         end
850     end
851
852     //clean previous tmp files
853     if isfile(tmp_tst) then
854         deletefile(tmp_tst);
855     end
856
857     if isfile(tmp_dia) then
858         deletefile(tmp_dia);
859     end
860
861     if isfile(tmp_res) then
862         deletefile(tmp_res);
863     end
864
865     if isfile(tmp_err) then
866         deletefile(tmp_err);
867     end
868
869     //create tmp test file
870     mputl(sciFile, tmp_tst);
871
872     //execute test
873     returnStatus = host(test_cmd);
874     //Check return status
875     if (returnStatus <> 0)
876         status.id = 5;
877         status.message = "failed: Slave Scilab exited with error code " + string(returnStatus);
878         return;
879     end
880
881     //Check errors
882     if (error_output == "check") & (_module.error_output == "check") then
883         if getos() == "Darwin" then
884             tmp_errfile_info = fileinfo(tmp_err);
885             msg = "Picked up _JAVA_OPTIONS:"; // When -Djava.awt.headless=false is forced for example
886
887             if ~isempty(tmp_errfile_info) then
888                 txt = mgetl(tmp_err);
889                 toRemove = grep(txt, msg);
890                 txt(toRemove) = [];
891                 if isempty(txt) then
892                     deletefile(tmp_err);
893                 else // Remove messages due to JOGL2 RC8
894                     toRemove = grep(txt, "__NSAutoreleaseNoPool()");
895                     txt(toRemove) = [];
896                     if isempty(txt) then
897                         deletefile(tmp_err);
898                     end
899                 end
900             end
901         end
902
903         if getos() == "Linux" then // Ignore JOGL2 debug message
904             tmp_errfile_info = fileinfo(tmp_err);
905             msg = "Error: unable to open display (null)"
906
907             if ~isempty(tmp_errfile_info) then
908                 txt = mgetl(tmp_err);
909                 txt(txt==msg) = [];
910
911                 // Remove messages due to warning message from external
912                 // libraries
913
914                 if ~isempty(txt) then
915                     toRemove = grep(txt, "libEGL warning: failed to find any driver");
916                     txt(toRemove) = [];
917                 end
918
919                 if ~isempty(txt) then
920                     toRemove = grep(txt, "extension ""RANDR"" missing on display");
921                     txt(toRemove) = [];
922                 end
923
924                 if ~isempty(txt) then
925                     toRemove = grep(txt, "libEGL warning: DRI2: failed to authenticate");
926                     txt(toRemove) = [];
927                 end
928
929                 if isempty(txt) then
930                     deletefile(tmp_err);
931                 end
932             end
933         end
934
935         if getos() == "Windows" then // Ignore JOGL 2.2.4 debug message
936             tmp_errfile_info = fileinfo(tmp_err);
937             msg = "Info: GLReadBufferUtil.readPixels: pre-exisiting GL error 0x500";
938
939             if ~isempty(tmp_errfile_info) then
940                 txt = mgetl(tmp_err);
941                 txt(txt==msg) = [];
942                 if isempty(txt) then
943                     deletefile(tmp_err);
944                 else // Ignore JOGL 2.1.4 debug message
945                     msg = "Info: GLDrawableHelper.reshape: pre-exisiting GL error 0x500";
946                     txt(txt==msg) = [];
947                     if isempty(txt) then
948                         deletefile(tmp_err);
949                     end
950                 end
951             end
952         end
953
954         if isfile(tmp_prof) then
955             txt = mgetl(tmp_prof);
956             if grep(txt($), "ERROR SUMMARY: 0 errors from 0 contexts") then
957                 deletefile(tmp_prof);
958             else
959                 status.id = 5;
960                 status.message = "failed: Valgrind error detected";
961                 status.details = checkthefile(tmp_prof);
962                 return;
963             end
964         end
965
966
967         tmp_errfile_info = fileinfo(tmp_err);
968
969         if isfile(tmp_err) & tmp_errfile_info(1) <> 0 then
970             status.id = 5;
971             status.message = "failed: error_output not empty\n   Use ''no_check_error_output'' option to disable this check.";
972             status.details = checkthefile(tmp_err);
973             return;
974         end
975     end
976
977     //Process output files
978
979     //Get the dia file
980     if isfile(tmp_dia) then
981         dia = mgetl(tmp_dia);
982     else
983         status.id = 6;
984         status.message = "failed: Cannot find the dia file: " + tmp_dia + "\nCheck if the Scilab used correctly starts";
985         status.details = checkthefile(tmp_dia);
986         return;
987     end
988
989     // To get TMPDIR value
990     tmpdir1_line = grep(dia, "TMPDIR1");
991     execstr(dia(tmpdir1_line));
992
993     //Check for execution errors
994     if try_catch & grep(dia,"<--Error on the test script file-->") <> [] then
995         details = [ checkthefile(tmp_dia); ..
996         launchthecommand(testFile)];
997         status.id = 3;
998         status.message = "failed: premature end of the test script";
999         status.details = details;
1000         if params.show_error == %t then
1001             status.details = [ status.details; dia($-10:$) ]
1002         end
1003
1004         return;
1005     end
1006
1007     // Remove Header and Footer
1008     dia = remove_headers(dia);
1009
1010     // Remove empty lines
1011     // In scilab 5, the test is executed in a try/catch
1012     // which remove empty lines.
1013     // In scilab 6, we can't execute the test in a try/catch
1014     // because it will be parsed first then executed
1015     // so the diary will contain all the script followed by the display
1016     // of the execution.
1017     // The try/catch is desactived ~line 513 by "try_catch     = %f;"
1018     // and the following line remove empty lines to reproduce the old operation.
1019     dia(find(dia == "")) = [];
1020
1021     //Check for execution errors
1022     dia_tmp = dia;
1023
1024     // remove commented lines
1025     dia_tmp(grep(dia_tmp, "//")) = [];
1026
1027     if try_catch & grep(dia_tmp, "!--error") <> [] then
1028         details = [ checkthefile(tmp_dia); ..
1029         launchthecommand(testFile) ];
1030         status.id = 1;
1031         status.message = "failed: the string (!--error) has been detected";
1032         status.details = details;
1033         return;
1034     end
1035
1036
1037     if grep(dia_tmp,"error on test")<>[] then
1038         details = [ checkthefile(tmp_dia); ..
1039         launchthecommand(testFile) ];
1040         status.id = 2;
1041         status.message = "failed: one or several tests failed";
1042         status.details = details;
1043         if params.show_error == %t then
1044             status.details = [ status.details; dia($-min(10, size(dia, "*")-1):$) ]
1045         end
1046         return;
1047     end
1048
1049
1050     if tmpdir1_line == [] then
1051         status.id = 6;
1052         status.message = "failed: the dia file is not correct";
1053         status.details = checkthefile(tmp_dia);
1054         return;
1055     end
1056
1057
1058     // Check the reference file only if check_ref (i.e. for the whole
1059     // test sequence) is true and this_check_ref (i.e. for the specific current .tst)
1060     // is true.
1061
1062     if (reference=="check") & (_module.reference=="check")  then
1063         if isfile(path_dia_ref) == %f then
1064             status.id = 5;
1065             status.message = "failed: the ref file doesn''t exist\n   Use ''no_check_ref'' option to disable this check.";
1066             status.details = createthefile(path_dia_ref);
1067             return;
1068         end
1069     end
1070
1071     // Comparaison ref <--> dia
1072
1073     if ( (reference=="check") & (_module.reference=="check") ) | (_module.reference=="create") then
1074         //  Do some modification in  dia file
1075
1076         dia(grep(dia, "printf(''%s\n'',tmpdirToPrint);")) = [];
1077         dia(grep(dia, "TMPDIR1")) = [];
1078         dia(grep(dia, "diary(0)")) = [];
1079
1080         if getos() == "Darwin" then // TMPDIR is a symblic link
1081             dia = strsubst(dia,"/private" + TMPDIR1, "TMPDIR");
1082             dia = strsubst(dia,"/private" + TMPDIR, "TMPDIR");
1083         end
1084         dia = strsubst(dia,TMPDIR ,"TMPDIR");
1085         dia = strsubst(dia,TMPDIR1, "TMPDIR");
1086
1087         if getos() == "Windows" then
1088             dia = strsubst(dia, strsubst(TMPDIR, "\","/"), "TMPDIR");
1089             dia = strsubst(dia, strsubst(TMPDIR1, "\","/"), "TMPDIR");
1090             dia = strsubst(dia, strsubst(TMPDIR, "/","\"), "TMPDIR");
1091             dia = strsubst(dia, strsubst(TMPDIR1, "/","\"), "TMPDIR");
1092             dia = strsubst(dia, strsubst(getshortpathname(TMPDIR), "\","/"), "TMPDIR");
1093             dia = strsubst(dia, strsubst(getshortpathname(TMPDIR1), "\","/"), "TMPDIR");
1094             dia = strsubst(dia, getshortpathname(TMPDIR), "TMPDIR");
1095             dia = strsubst(dia, getshortpathname(TMPDIR1), "TMPDIR");
1096         end
1097
1098         dia = strsubst(dia, SCI, "SCI");
1099
1100         if getos() == "Windows" then
1101             dia = strsubst(dia, strsubst(SCI, "\","/"), "SCI");
1102             dia = strsubst(dia, strsubst(SCI, "/","\"), "SCI");
1103             dia = strsubst(dia, strsubst(getshortpathname(SCI), "\","/"), "SCI");
1104             dia = strsubst(dia, getshortpathname(SCI), "SCI");
1105         end
1106
1107         //suppress the prompts
1108         dia = strsubst(dia, "--> ", "");
1109         dia = strsubst(dia, "@#> ", "--> ");
1110         dia = strsubst(dia, "-1-> ", "");
1111
1112         //standardise  number display
1113
1114         // strsubst(dia, " .", "0.");
1115         // strsubst(dia, "-.", "-0.")
1116         // strsubst(dia, "E+", "D+");
1117         // strsubst(dia, "E-", "D-");
1118
1119         //not to change the ref files
1120         dia = strsubst(dia ,"bugmes();return", "bugmes();quit");
1121
1122         if _module.reference=="create" then
1123             // Delete previous .dia.ref file
1124             if isfile(path_dia_ref) then
1125                 deletefile(path_dia_ref)
1126             end
1127
1128             mputl(dia, path_dia_ref);
1129             status.id = 20;
1130             status.message = "passed: ref created";
1131             return;
1132         else
1133             // write down the resulting dia file
1134             mputl(dia, path_dia);
1135
1136             //Check for diff with the .ref file
1137             [u,ierr] = mopen(path_dia_ref, "r");
1138             if ierr== 0 then //ref file exists
1139                 ref=mgetl(u);
1140                 mclose(u)
1141
1142                 // suppress blank (diff -nw)
1143                 dia = strsubst(dia, " ", "")
1144                 ref = strsubst(ref, " ", "")
1145
1146                 dia(find(dia == "")) = [];
1147                 ref(find(ref == "")) = [];
1148
1149                 dia(find(dia == "")) = [];
1150                 ref(find(ref == "")) = [];
1151
1152                 dia(find(part(dia, (1:2)) == "//")) = [];
1153                 ref(find(part(ref, (1:2)) == "//")) = [];
1154
1155                 if or(ref <> dia) then
1156                     status.id = 4;
1157                     status.message = "failed: dia and ref are not equal";
1158                     status.details = comparethefiles(path_dia, path_dia_ref);
1159                     return;
1160                 end
1161
1162             else
1163                 error(sprintf(gettext("The ref file (%s) doesn''t exist"), path_dia_ref));
1164             end
1165         end
1166     end
1167 endfunction
1168
1169 // checkthefile
1170 function msg = checkthefile( filename )
1171     // Returns a 2-by-1 matrix of strings, containing a message such as:
1172     //   Check the following file :
1173     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.tst
1174     // Workaround for bug #4827
1175     msg(1) = "   Check the following file :"
1176     msg(2) = "   - "+filename
1177     if params.show_error == %t then
1178         msg=[msg; mgetl(filename)]
1179     end
1180
1181 endfunction
1182
1183 // launchthecommand
1184 function msg = launchthecommand( filename )
1185     // Returns a 2-by-1 matrix of strings, containing a message such as:
1186     //   Or launch the following command :
1187     //   - exec("C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.tst")
1188     // Workaround for bug #4827
1189     msg(1) = "   Or launch the following command :"
1190     msg(2) = "   - exec(""" + fullpath(filename) + """);"
1191 endfunction
1192
1193 // => remove header from the diary txt
1194 function dia_out = remove_headers(dia_in)
1195     dia_out = dia_in;
1196     body_start = grep(dia_out,"// <-- HEADER END -->");
1197     if body_start <> [] then
1198         dia_out(1:body_start(1)) = [];
1199     end
1200
1201     body_end   = grep(dia_out,"// <-- FOOTER START -->");
1202     if body_end <> [] then
1203         [dia_nl,dia_nc] = size(dia);
1204         dia_out(body_end(1):dia_nl) = [];
1205     end
1206 endfunction
1207
1208 //createthefile
1209 function msg = createthefile ( filename )
1210     // Returns a 2-by-1 matrix of strings, containing a message such as:
1211     //   Add or create the following file :
1212     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia.ref
1213     // Workaround for bug #4827
1214     msg(1) = "   Add or create the following file: "
1215     msg(2) = "   - "+filename
1216 endfunction
1217
1218 // comparethefiles
1219 function msg = comparethefiles ( filename1 , filename2 )
1220     // Returns a 3-by-1 matrix of strings, containing a message such as:
1221     //   Compare the following files :
1222     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia
1223     //   - C:\path\scilab\modules\optimization\tests\unit_testseldermeadeldermead_configure.dia.ref
1224     // Workaround for bug #4827
1225     msg(1) = "   Compare the following files:"
1226     msg(2) = "   - "+filename1
1227     msg(3) = "   - "+filename2
1228     if params.show_diff == %t then
1229         if getos() == "Windows" then
1230             diffTool = SCI + "\tools\diff\diff.exe";
1231         else
1232             diffTool = "diff";
1233         end
1234         targetFile=TMPDIR + filesep() + "tempdiff.diff";
1235         unix(diffTool + " -u " + filename1 + " " + filename2 + " > " + targetFile);
1236         // unix_g is failing to return the output into a variable
1237         msg=[msg; mgetl(targetFile)]
1238         deletefile(targetFile);
1239     end
1240 endfunction
1241
1242 function directories = getDirectories(directory)
1243     directories = directory;
1244     items = gsort(listfiles(directory),"lr","i");
1245
1246     for i=1:size(items,"*")
1247         if isdir(directory + items(i)) then
1248             directories = [directories; getDirectories(directory + items(i) + filesep())];
1249         end
1250     end
1251 endfunction
1252
1253 function name = splitModule(name)
1254     if ~isempty( regexp(stripblanks(name),"/\|/") ) then
1255         name = stripblanks( strsubst( strsplit(name,regexp(stripblanks(name),"/\|/")) , "/\|$/","","r" ) );
1256     end
1257 endfunction
1258
1259 function example = test_examples()
1260     example = [ sprintf("Examples :\n\n") ];
1261     example = [ example ; sprintf("// Launch all tests\n") ];
1262     example = [ example ; sprintf("test_run();\n") ];
1263     example = [ example ; sprintf("test_run([]);\n") ];
1264     example = [ example ; sprintf("test_run([],[]);\n") ];
1265     example = [ example ; "" ];
1266     example = [ example ; sprintf("// Test one or several module\n") ];
1267     example = [ example ; sprintf("test_run(''core'');\n") ];
1268     example = [ example ; sprintf("test_run(''core'',[]);\n") ];
1269     example = [ example ; sprintf("test_run([''core'',''string'']);\n") ];
1270     example = [ example ; "" ];
1271     example = [ example ; sprintf("// Launch one or several test in a specified module\n") ];
1272     example = [ example ; sprintf("test_run(''core'',[''trycatch'',''opcode'']);\n") ];
1273     example = [ example ; "" ];
1274     example = [ example ; sprintf("// With options\n") ];
1275     example = [ example ; sprintf("test_run([],[],''no_check_ref'');\n") ];
1276     example = [ example ; sprintf("test_run([],[],''no_check_error_output'');\n") ];
1277     example = [ example ; sprintf("test_run([],[],''create_ref'');\n") ];
1278     example = [ example ; sprintf("test_run([],[],''list'');\n") ];
1279     example = [ example ; sprintf("test_run([],[],''help'');\n") ];
1280     example = [ example ; sprintf("test_run([],[],[''no_check_ref'',''mode_nw'']);\n") ];
1281     example = [ example ; "" ];
1282 endfunction
1283
1284 function newOption = clean_option(var, option)
1285     newOption = var;
1286     newOption(newOption == option) = [];
1287 endfunction
1288
1289 function result = check_option(var, option)
1290     result = or(var == option);
1291 endfunction
1292
1293 function value = assign_option(var, option, truevalue, falsevalue)
1294     if check_option(var, option) then
1295         value = truevalue;
1296     else
1297         value = falsevalue;
1298     end
1299 endfunction
1300
1301
1302 function exportToXUnitFormat(exportToFile, testsuites)
1303
1304     if isfile(exportToFile) then
1305         // File already existing. Append the results
1306         doc = xmlRead(exportToFile);
1307         appendIntoFile = %t;
1308         node = xmlXPath(doc, "//testsuites");
1309         if node.size == 0 then
1310             error(msprintf(gettext("The file ''%s'' is not following the XUnit XML format. Root tag expected ''testsuites''.\n"),exportToFile))
1311         end
1312     else
1313         doc = xmlDocument(exportToFile);
1314
1315         appendIntoFile = %f;
1316     end
1317     root = xmlElement(doc, "testsuites");
1318
1319     for i=1:size(testsuites, "*") // Export module by module
1320         module = testsuites(i);
1321         testsuite = xmlElement(doc,"testsuite");
1322         testsuite.attributes.name = module.name;
1323
1324         testsuite.attributes.time  = string(module.time);
1325
1326         testsuite.attributes.tests = string(module.tests);
1327         testsuite.attributes.errors = string(module.errors);
1328
1329         for j=1:size(module.testcase,"*") // Export test by test
1330             testsuite.children(j) = xmlElement(doc,"testcase");
1331             unitTest = module.testcase(j);
1332             testsuite.children(j).attributes.name = unitTest.name;
1333             testsuite.children(j).attributes.time = string(unitTest.time);
1334             testsuite.children(j).attributes.classname = getversion()+"."+module.name;
1335             if isfield(unitTest,"failure") & size(unitTest.failure,"*") >= 1 then
1336                 testsuite.children(j).children(1) = xmlElement(doc,"failure");
1337                 testsuite.children(j).children(1).attributes.type = unitTest.failure.type;
1338                 content = unitTest.failure.content;
1339                 for kL=1:size(content, "*")
1340                     ampIdx = strindex(content(kL), "&");
1341                     while ~isempty(ampIdx)
1342                         cur = ampIdx(1);
1343                         ampIdx(1) = [];
1344                         if or(part(content(kL), (cur+1):(cur+3))==["gt;" "lt"]) then
1345                             // Ignored
1346                         else
1347                             content(kL) = part(content(kL), 1:cur) + "amp;" + part(content(kL), (cur+1):$);
1348                             ampIdx = strindex(part(content(kL), (cur+1):$), "&");
1349                         end
1350                     end
1351                 end
1352                 testsuite.children(j).children(1).content = content;
1353             elseif unitTest.skipped then
1354                 testsuite.children(j).children(1) = xmlElement(doc,"skipped");
1355             end
1356         end
1357
1358         if appendIntoFile then
1359             // We will add the new elements into 'testsuites'
1360             c=node(1).children;
1361             nb=size(c,"*");
1362             c(nb + 1)=testsuite; // Add the new results into the list of results
1363             root.children=c;
1364         else
1365             root.children(i)=testsuite
1366         end
1367     end // list of modules
1368
1369     doc.root=root
1370
1371     xmlWrite(doc);
1372 endfunction