1###########################################################################
2# Module to test API Sanity Checker
3#
4# Copyright (C) 2009-2010 The Linux Foundation
5# Copyright (C) 2009-2011 Institute for System Programming, RAS
6# Copyright (C) 2011-2013 ROSA Lab
7#
8# Written by Andrey Ponomarenko
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License or the GNU Lesser
12# General Public License as published by the Free Software Foundation.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# and the GNU Lesser General Public License along with this program.
21# If not, see <http://www.gnu.org/licenses/>.
22###########################################################################
23use strict;
24
25my ($Debug, $LIB_EXT, $OpenReport, $TargetCompiler);
26my $OSgroup = get_OSgroup();
27
28sub testTool($$$$)
29{
30    ($Debug, $LIB_EXT, $OpenReport, $TargetCompiler) = @_;
31
32    testC();
33    testCpp();
34}
35
36sub testCpp()
37{
38    printMsg("INFO", "testing C++ library API");
39    my ($DataDefs, $Sources)  = ();
40    my $DeclSpec = ($OSgroup eq "windows")?"__declspec( dllexport )":"";
41
42    # Inline
43    $DataDefs .= "
44        inline int inline_func(int param) { return 0; }";
45
46    # Simple parameters
47    $DataDefs .= "
48        $DeclSpec int func_simple_parameters(
49            int a,
50            float b,
51            double c,
52            long double d,
53            long long e,
54            char f,
55            unsigned int g,
56            const char* h,
57            char* i,
58            unsigned char* j,
59            char** k,
60            const char*& l,
61            const char**& m,
62            char const*const* n,
63            unsigned int* offset
64        );";
65    $Sources .= "
66        int func_simple_parameters(
67            int a,
68            float b,
69            double c,
70            long double d,
71            long long e,
72            char f,
73            unsigned int g,
74            const char* h,
75            char* i,
76            unsigned char* j,
77            char** k,
78            const char*& l,
79            const char**& m,
80            char const*const* n,
81            unsigned int* offset ) {
82            return 1;
83        }";
84
85    # Initialization by interface
86    $DataDefs .= "
87        struct simple_struct {
88            int m;
89        };
90        $DeclSpec struct simple_struct simple_func(int a, int b);";
91    $Sources .= "
92        struct simple_struct simple_func(int a, int b)
93        {
94            struct simple_struct x = {1};
95            return x;
96        }";
97
98    $DataDefs .= "
99        $DeclSpec int func_init_param_by_interface(struct simple_struct p);";
100    $Sources .= "
101        int func_init_param_by_interface(struct simple_struct p) {
102            return 1;
103        }";
104
105    # Private Interface
106    $DataDefs .= "
107        class $DeclSpec private_class {
108        private:
109            private_class(){};
110            int a;
111            float private_func(float p);
112        };";
113    $Sources .= "
114        float private_class::private_func(float p) {
115            return p;
116        }";
117
118    # Assembling structure
119    $DataDefs .= "
120        struct complex_struct {
121            int a;
122            float b;
123            struct complex_struct* c;
124        };";
125
126    $DataDefs .= "
127        $DeclSpec int func_assemble_param(struct complex_struct p);";
128    $Sources .= "
129        int func_assemble_param(struct complex_struct p) {
130            return 1;
131        }";
132
133    # Abstract class
134    $DataDefs .= "
135        class $DeclSpec abstract_class {
136        public:
137            abstract_class(){};
138            int a;
139            virtual float virt_func(float p) = 0;
140            float func(float p);
141        };";
142    $Sources .= "
143        float abstract_class::func(float p) {
144            return p;
145        }";
146
147    # Parameter FuncPtr
148    $DataDefs .= "
149        typedef int (*funcptr_type)(int a, int b);
150        $DeclSpec funcptr_type func_return_funcptr(int a);
151        $DeclSpec int func_param_funcptr(const funcptr_type** p);";
152    $Sources .= "
153        funcptr_type func_return_funcptr(int a) {
154            return 0;
155        }
156        int func_param_funcptr(const funcptr_type** p) {
157            return 0;
158        }";
159
160    # Parameter FuncPtr (2)
161    $DataDefs .= "
162        typedef int (*funcptr_type2)(int a, int b, float c);
163        $DeclSpec int func_param_funcptr2(funcptr_type2 p);";
164    $Sources .= "
165        int func_param_funcptr2(funcptr_type2 p) {
166            return 0;
167        }";
168
169    # Parameter Array
170    $DataDefs .= "
171        $DeclSpec int func_param_array(struct complex_struct const ** x);";
172    $Sources .= "
173        int func_param_array(struct complex_struct const ** x) {
174            return 0;
175        }";
176
177    # Nested classes
178    $DataDefs .= "//Nested classes
179        class $DeclSpec A {
180        public:
181            virtual bool method1() {
182                return false;
183            };
184        };
185
186        class $DeclSpec B: public A { };
187
188        class $DeclSpec C: public B {
189        public:
190            C() { };
191            virtual bool method1();
192            virtual bool method2() const;
193        };";
194    $Sources .= "//Nested classes
195        bool C::method1() {
196            return false;
197        };
198
199        bool C::method2() const {
200            return false;
201        };";
202
203    # Throw class
204    $DataDefs .= "
205        class $DeclSpec Exception {
206        public:
207            Exception();
208            int a;
209        };";
210    $Sources .= "
211        Exception::Exception() { }";
212    $DataDefs .= "
213        class $DeclSpec throw_class {
214        public:
215            throw_class() { };
216            int a;
217            virtual float virt_func(float p) throw(Exception) = 0;
218            float func(float p);
219        };";
220    $Sources .= "
221        float throw_class::func(float p) {
222            return p;
223        }";
224
225    # Should crash
226    $DataDefs .= "
227        $DeclSpec int func_should_crash();";
228    $Sources .= "
229        int func_should_crash()
230        {
231            int *x = 0x0;
232            *x = 1;
233            return 1;
234        }";
235
236    runTests("libsample_cpp", "C++", "namespace TestNS {\n$DataDefs\n}\n", "namespace TestNS {\n$Sources\n}\n", "type_test_opaque", "_ZN18type_test_internal5func1ES_");
237}
238
239sub testC()
240{
241    printMsg("INFO", "\ntesting C library API");
242    my ($DataDefs, $Sources)  = ();
243    my $DeclSpec = ($OSgroup eq "windows")?"__declspec( dllexport )":"";
244
245    # Simple parameters
246    $DataDefs .= "
247        $DeclSpec int func_simple_parameters(
248            int a,
249            float b,
250            double c,
251            long double d,
252            long long e,
253            char f,
254            unsigned int g,
255            const char* h,
256            char* i,
257            unsigned char* j,
258            char** k);";
259    $Sources .= "
260        int func_simple_parameters(
261            int a,
262            float b,
263            double c,
264            long double d,
265            long long e,
266            char f,
267            unsigned int g,
268            const char* h,
269            char* i,
270            unsigned char* j,
271            char** k) {
272            return 1;
273        }";
274
275    # Initialization by interface
276    $DataDefs .= "
277        struct simple_struct {
278            int m;
279        };
280        $DeclSpec struct simple_struct simple_func(int a, int b);";
281    $Sources .= "
282        struct simple_struct simple_func(int a, int b)
283        {
284            struct simple_struct x = {1};
285            return x;
286        }";
287
288    $DataDefs .= "
289        $DeclSpec int func_init_param_by_interface(struct simple_struct p);";
290    $Sources .= "
291        int func_init_param_by_interface(struct simple_struct p) {
292            return 1;
293        }";
294
295    # Assembling structure
296    $DataDefs .= "
297        typedef struct complex_struct {
298            int a;
299            float b;
300            struct complex_struct* c;
301        } complex_struct;";
302
303    $DataDefs .= "
304        $DeclSpec int func_assemble_param(struct complex_struct p);";
305    $Sources .= "
306        int func_assemble_param(struct complex_struct p) {
307            return 1;
308        }";
309
310    # Initialization by out parameter
311    $DataDefs .= "
312        struct out_opaque_struct;
313        $DeclSpec void create_out_param(struct out_opaque_struct** out);";
314    $Sources .= "
315        struct out_opaque_struct {
316            const char* str;
317        };
318        $DeclSpec void create_out_param(struct out_opaque_struct** out) { }\n";
319
320    $DataDefs .= "
321        $DeclSpec int func_init_param_by_out_param(struct out_opaque_struct* p);";
322    $Sources .= "
323        int func_init_param_by_out_param(struct out_opaque_struct* p) {
324            return 1;
325        }";
326
327    # Should crash
328    $DataDefs .= "
329        $DeclSpec int func_should_crash();";
330    $Sources .= "
331        int func_should_crash()
332        {
333            int *x = 0x0;
334            *x = 1;
335            return 1;
336        }";
337
338    # Function with out parameter
339    $DataDefs .= "
340        $DeclSpec int func_has_out_opaque_param(struct out_opaque_struct* out);";
341    $Sources .= "
342        int func_has_out_opaque_param(struct out_opaque_struct* out) {
343            return 1;
344        }";
345
346    # C++ keywords
347    $DataDefs .= "
348        $DeclSpec int operator();";
349    $Sources .= "
350        int operator() {
351            return 1;
352        }";
353
354
355    runTests("libsample_c", "C", $DataDefs, $Sources, "type_test_opaque", "func_test_internal");
356}
357
358sub readFirstLine($)
359{
360    my $Path = $_[0];
361    return "" if(not $Path or not -f $Path);
362    open (FILE, $Path);
363    my $FirstLine = <FILE>;
364    close(FILE);
365    return $FirstLine;
366}
367
368sub runTests($$$$$$)
369{
370    my ($LibName, $Lang, $DataDefs, $Sources, $Opaque, $Private) = @_;
371    my $Ext = ($Lang eq "C++")?"cpp":"c";
372    # creating test suite
373    rmtree($LibName);
374    mkpath($LibName);
375    writeFile("$LibName/version", "TEST_1.0 {\n};\nTEST_2.0 {\n};\n");
376    writeFile("$LibName/libsample.h", $DataDefs."\n");
377    writeFile("$LibName/libsample.$Ext", "#include \"libsample.h\"\n".$Sources."\n");
378    writeFile("$LibName/descriptor.xml", "
379        <version>
380            1.0
381        </version>
382
383        <headers>
384            ".abs_path($LibName)."
385        </headers>
386
387        <libs>
388            ".abs_path($LibName)."
389        </libs>
390
391        <opaque_types>
392            $Opaque
393        </opaque_types>
394
395        <skip_symbols>
396            $Private
397        </skip_symbols>\n");
398    my @BuildCmds = ();
399    if($OSgroup eq "windows")
400    {
401        if($TargetCompiler eq "CL")
402        {
403            push(@BuildCmds, "cl /LD libsample.$Ext >build_out 2>&1");
404        }
405        else
406        {
407            if($Lang eq "C++")
408            {
409                push(@BuildCmds, "g++ -shared -fpic libsample.$Ext -o libsample.$LIB_EXT");
410                push(@BuildCmds, "g++ -c libsample.$Ext -o libsample.obj");
411            }
412            else
413            {
414                push(@BuildCmds, "gcc -shared -fpic libsample.$Ext -o libsample.$LIB_EXT");
415                push(@BuildCmds, "gcc -c libsample.$Ext -o libsample.obj");
416                push(@BuildCmds, "lib libsample.obj >build_out 2>&1");
417            }
418        }
419    }
420    elsif($OSgroup eq "linux")
421    {
422        writeFile("$LibName/version", "VERSION_1.0 {\n};\nVERSION_2.0 {\n};\n");
423        my $BCmd = "";
424        if($Lang eq "C++") {
425            $BCmd = "g++ -Wl,--version-script version -shared -fpic libsample.$Ext -o libsample.$LIB_EXT";
426        }
427        else {
428            $BCmd = "gcc -Wl,--version-script version -shared -fpic libsample.$Ext -o libsample.$LIB_EXT";
429        }
430        if(getArch()=~/\A(arm|x86_64)\Z/i)
431        { # relocation R_X86_64_32S against `vtable for class' can not be used when making a shared object; recompile with -fPIC
432            $BCmd .= " -fPIC";
433        }
434        push(@BuildCmds, $BCmd);
435    }
436    elsif($OSgroup eq "macos")
437    {
438        if($Lang eq "C++") {
439            push(@BuildCmds, "g++ -dynamiclib libsample.$Ext -o libsample.$LIB_EXT");
440        }
441        else {
442            push(@BuildCmds, "gcc -dynamiclib libsample.$Ext -o libsample.$LIB_EXT");
443        }
444    }
445    else
446    {
447        if($Lang eq "C++") {
448            push(@BuildCmds, "g++ -shared -fpic libsample.$Ext -o libsample.$LIB_EXT");
449        }
450        else {
451            push(@BuildCmds, "gcc -shared -fpic libsample.$Ext -o libsample.$LIB_EXT");
452        }
453    }
454    writeFile("$LibName/Makefile", "all:\n\t".join("\n\t", @BuildCmds)."\n");
455    foreach (@BuildCmds)
456    {
457        system("cd $LibName && $_");
458        if($?) {
459            exitStatus("Error", "can't compile \'$LibName/libsample.$Ext\'");
460        }
461    }
462    # running the tool
463    my $Cmd = "perl $0 -l $LibName -d $LibName/descriptor.xml -gen -build -run -show-retval";
464    if($OpenReport) {
465        $Cmd .= " -open";
466    }
467    if($TargetCompiler) {
468        $Cmd .= " -target ".$TargetCompiler;
469    }
470    if($Debug)
471    {
472        $Cmd .= " -debug";
473        printMsg("INFO", "run $Cmd");
474    }
475    system($Cmd);
476
477    my $ECode = $?>>8;
478
479    if($ECode!~/\A[0-1]\Z/)
480    { # error
481        exitStatus("Error", "analysis has failed");
482    }
483
484    my ($Total, $Passed, $Failed) = (0, 0, 0);
485    if(my $FLine = readFirstLine("test_results/$LibName/1.0/test_results.html"))
486    {
487        if($FLine=~/total:(\d+)/) {
488            $Total = $1;
489        }
490        if($FLine=~/passed:(\d+)/) {
491            $Passed = $1;
492        }
493        if($FLine=~/failed:(\d+)/) {
494            $Failed = $1;
495        }
496    }
497    if($Total==($Passed+$Failed) and (($LibName eq "libsample_c" and $Total>5 and $Failed>=1)
498    or ($LibName eq "libsample_cpp" and $Total>10 and $Failed>=1))) {
499        printMsg("INFO", "result: SUCCESS ($Total test cases, $Passed passed, $Failed failed)\n");
500    }
501    else {
502        printMsg("ERROR", "result: FAILED ($Total test cases, $Passed passed, $Failed failed)\n");
503    }
504}
505
506return 1;