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