pidl/python: Work towards supporting more complex types (arrays, etc).
[ira/wip.git] / source / pidl / tests / samba-ndr.pl
1 #!/usr/bin/perl
2 # (C) 2007 Jelmer Vernooij <jelmer@samba.org>
3 # Published under the GNU General Public License
4 use strict;
5 use warnings;
6
7 use Test::More tests => 41;
8 use FindBin qw($RealBin);
9 use lib "$RealBin";
10 use Util;
11 use strict;
12 use Parse::Pidl::Util qw(MyDumper);
13 use Parse::Pidl::Samba4::NDR::Parser qw(check_null_pointer 
14         GenerateFunctionInEnv GenerateFunctionOutEnv GenerateStructEnv 
15         EnvSubstituteValue NeededFunction NeededElement NeededType
16         NeededInterface TypeFunctionName ParseElementPrint); 
17
18 my $output;
19 sub print_fn($) { my $x = shift; $output.=$x; }
20
21 # Test case 1: Simple unique pointer dereference
22
23 $output = "";
24 my $fn = check_null_pointer({ 
25         PARENT => {
26                 ELEMENTS => [
27                         { 
28                                 NAME => "bla",
29                                 LEVELS => [
30                                         { TYPE => "POINTER",
31                                           POINTER_INDEX => 0,
32                                           POINTER_TYPE => "unique" },
33                                         { TYPE => "DATA" }
34                                 ],
35                         },
36                 ]
37         }
38 }, { bla => "r->in.bla" }, \&print_fn, "return;"); 
39
40
41 test_warnings("", sub { $fn->("r->in.bla"); });
42
43 is($output, "if (r->in.bla == NULL) return;");
44
45 # Test case 2: Simple ref pointer dereference
46
47 $output = "";
48 $fn = check_null_pointer({ 
49         PARENT => {
50                 ELEMENTS => [
51                         { 
52                                 NAME => "bla",
53                                 LEVELS => [
54                                         { TYPE => "POINTER",
55                                           POINTER_INDEX => 0,
56                                           POINTER_TYPE => "ref" },
57                                         { TYPE => "DATA" }
58                                 ],
59                         },
60                 ]
61         }
62 }, { bla => "r->in.bla" }, \&print_fn, undef); 
63
64 test_warnings("", sub { $fn->("r->in.bla"); });
65
66 is($output, "");
67
68 # Test case 3: Illegal dereference
69
70 $output = "";
71 $fn = check_null_pointer({ 
72         FILE => "nofile",
73         LINE => 1,
74         PARENT => {
75                 ELEMENTS => [
76                         { 
77                                 NAME => "bla",
78                                 LEVELS => [
79                                         { TYPE => "DATA" }
80                                 ],
81                         },
82                 ]
83         }
84 }, { bla => "r->in.bla" }, \&print_fn, undef); 
85
86 test_warnings("nofile:1: too much dereferences for `bla'\n", 
87                   sub { $fn->("r->in.bla"); });
88
89 is($output, "");
90
91 # Test case 4: Double pointer dereference
92
93 $output = "";
94 $fn = check_null_pointer({ 
95         PARENT => {
96                 ELEMENTS => [
97                         { 
98                                 NAME => "bla",
99                                 LEVELS => [
100                                         { TYPE => "POINTER",
101                                           POINTER_INDEX => 0,
102                                           POINTER_TYPE => "unique" },
103                                         { TYPE => "POINTER",
104                                           POINTER_INDEX => 1,
105                                           POINTER_TYPE => "unique" },
106                                         { TYPE => "DATA" }
107                                 ],
108                         },
109                 ]
110         }
111 }, { bla => "r->in.bla" }, \&print_fn, "return;"); 
112
113 test_warnings("",
114                   sub { $fn->("*r->in.bla"); });
115
116 is($output, "if (*r->in.bla == NULL) return;");
117
118 # Test case 5: Unknown variable
119
120 $output = "";
121 $fn = check_null_pointer({ 
122         FILE => "nofile",
123         LINE => 2,
124         PARENT => {
125                 ELEMENTS => [
126                         { 
127                                 NAME => "bla",
128                                 LEVELS => [
129                                         { TYPE => "DATA" }
130                                 ],
131                         },
132                 ]
133         }
134 }, { }, \&print_fn, "return;"); 
135
136 test_warnings("nofile:2: unknown dereferenced expression `r->in.bla'\n",
137                   sub { $fn->("r->in.bla"); });
138
139 is($output, "if (r->in.bla == NULL) return;");
140
141 # Make sure GenerateFunctionInEnv and GenerateFunctionOutEnv work
142 $fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] };
143 is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionInEnv($fn));
144
145 $fn = { ELEMENTS => [ { DIRECTION => ["out"], NAME => "foo" } ] };
146 is_deeply({ "foo" => "r->out.foo" }, GenerateFunctionOutEnv($fn));
147
148 $fn = { ELEMENTS => [ { DIRECTION => ["out", "in"], NAME => "foo" } ] };
149 is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionInEnv($fn));
150
151 $fn = { ELEMENTS => [ { DIRECTION => ["out", "in"], NAME => "foo" } ] };
152 is_deeply({ "foo" => "r->out.foo" }, GenerateFunctionOutEnv($fn));
153
154 $fn = { ELEMENTS => [ { DIRECTION => ["in"], NAME => "foo" } ] };
155 is_deeply({ "foo" => "r->in.foo" }, GenerateFunctionOutEnv($fn));
156
157 $fn = { ELEMENTS => [ { DIRECTION => ["out"], NAME => "foo" } ] };
158 is_deeply({ }, GenerateFunctionInEnv($fn));
159
160 $fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] };
161 is_deeply({ foo => "r->foo", bar => "r->bar", this => "r" }, 
162                 GenerateStructEnv($fn, "r"));
163
164 $fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] };
165 is_deeply({ foo => "some->complex.variable->foo", 
166                     bar => "some->complex.variable->bar", 
167                         this => "some->complex.variable" }, 
168                 GenerateStructEnv($fn, "some->complex.variable"));
169
170 $fn = { ELEMENTS => [ { NAME => "foo", PROPERTIES => { value => 3 }} ] };
171
172 my $env = GenerateStructEnv($fn, "r");
173 EnvSubstituteValue($env, $fn);
174 is_deeply($env, { foo => 3, this => "r" });
175
176 $fn = { ELEMENTS => [ { NAME => "foo" }, { NAME => "bar" } ] };
177 $env = GenerateStructEnv($fn, "r");
178 EnvSubstituteValue($env, $fn);
179 is_deeply($env, { foo => 'r->foo', bar => 'r->bar', this => "r" });
180
181 $fn = { ELEMENTS => [ { NAME => "foo", PROPERTIES => { value => 0 }} ] };
182
183 $env = GenerateStructEnv($fn, "r");
184 EnvSubstituteValue($env, $fn);
185 is_deeply($env, { foo => 0, this => "r" });
186
187 my $needed = {};
188 NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "pull", $needed); 
189 is_deeply($needed, { ndr_pull_foo => 1 });
190
191 # old settings should be kept
192 $needed = { ndr_pull_foo => 0 };
193 NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "pull", $needed); 
194 is_deeply($needed, { ndr_pull_foo => 0 });
195
196 # print/pull/push are independent of each other
197 $needed = { ndr_pull_foo => 0 };
198 NeededElement({ TYPE => "foo", REPRESENTATION_TYPE => "foo" }, "print", $needed); 
199 is_deeply($needed, { ndr_pull_foo => 0, ndr_print_foo => 1 });
200
201 $needed = { };
202 NeededFunction({ NAME => "foo", ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] }, $needed); 
203 is_deeply($needed, { ndr_pull_foo => 1, ndr_print_foo => 1, ndr_push_foo => 1,
204                          ndr_pull_bar => 1, ndr_print_bar => 1, ndr_push_bar => 1});
205
206 # push/pull/print are always set for functions
207 $needed = { ndr_pull_foo => 0 };
208 NeededFunction({ NAME => "foo", ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] }, $needed); 
209 is_deeply($needed, { ndr_pull_foo => 1, ndr_print_foo => 1, ndr_push_foo => 1,
210                          ndr_pull_bar => 1, ndr_push_bar => 1, ndr_print_bar => 1});
211
212 # public structs are always needed
213 $needed = {};
214 NeededType({ NAME => "bla", TYPE => "TYPEDEF",
215                 DATA => { TYPE => "STRUCT", ELEMENTS => [] } },
216                           $needed, "pull");
217 is_deeply($needed, { });
218
219 $needed = {};
220 NeededInterface({ TYPES => [ { PROPERTIES => { public => 1 }, NAME => "bla", 
221                                 TYPE => "TYPEDEF",
222                     DATA => { TYPE => "STRUCT", ELEMENTS => [] } } ] },
223                           $needed);
224 is_deeply($needed, { ndr_pull_bla => 1, ndr_push_bla => 1, ndr_print_bla => 1 });
225
226 # make sure types for elements are set too
227 $needed = {};
228 NeededInterface({ TYPES => [ { PROPERTIES => { public => 1 }, NAME => "bla", 
229                                 TYPE => "TYPEDEF",
230                     DATA => { TYPE => "STRUCT", 
231                                                   ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } } ] },
232                           $needed);
233 is_deeply($needed, { ndr_pull_bla => 1, ndr_pull_bar => 1, ndr_push_bla => 1, ndr_push_bar => 1,
234                                          ndr_print_bla => 1, ndr_print_bar => 1});
235
236 $needed = {};
237 NeededInterface({ TYPES => [ { PROPERTIES => { gensize => 1}, NAME => "bla", 
238                                 TYPE => "TYPEDEF",
239                     DATA => { TYPE => "STRUCT", 
240                                                   ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } } ] },
241                           $needed);
242 is_deeply($needed, { ndr_size_bla => 1 });
243                          
244 # make sure types for elements are set too
245 $needed = { ndr_pull_bla => 1 };
246 NeededType({ NAME => "bla", 
247                                 TYPE => "TYPEDEF",
248                     DATA => { TYPE => "STRUCT", 
249                                                   ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "bar" } ] } },
250                           $needed, "pull");
251 is_deeply($needed, { ndr_pull_bla => 1, ndr_pull_bar => 1 });
252
253 $needed = {};
254 NeededInterface({ TYPES => [ { PROPERTIES => { public => 1}, 
255                                 NAME => "bla", 
256                                 TYPE => "TYPEDEF",
257                     DATA => { TYPE => "STRUCT", 
258                                                   ELEMENTS => [ { TYPE => "bar", REPRESENTATION_TYPE => "rep" } ] } } ] }, $needed);
259 is_deeply($needed, { ndr_pull_bla => 1, ndr_push_bla => 1, ndr_print_bla => 1, 
260                                          ndr_print_rep => 1,
261                          ndr_pull_bar => 1, ndr_push_bar => 1, 
262                                      ndr_bar_to_rep => 1, ndr_rep_to_bar => 1});
263         
264 my $generator = new Parse::Pidl::Samba4::NDR::Parser();
265 $generator->ParseStructPush({
266                         NAME => "mystruct",
267                         TYPE => "STRUCT",
268                         PROPERTIES => {},
269                         ALIGN => 4,
270                         ELEMENTS => [ ]}, "x");
271 is($generator->{res}, "if (ndr_flags & NDR_SCALARS) {
272         NDR_CHECK(ndr_push_align(ndr, 4));
273 }
274 if (ndr_flags & NDR_BUFFERS) {
275 }
276 ");
277
278 $generator = new Parse::Pidl::Samba4::NDR::Parser();
279 my $e = { 
280         NAME => "el1", 
281         TYPE => "mytype",
282         REPRESENTATION_TYPE => "mytype",
283         PROPERTIES => {},
284         LEVELS => [ 
285                 { LEVEL_INDEX => 0, TYPE => "DATA", DATA_TYPE => "mytype" } 
286 ] };
287 $generator->ParseStructPush({
288                         NAME => "mystruct",
289                         TYPE => "STRUCT",
290                         PROPERTIES => {},
291                         ALIGN => 4,
292                         SURROUNDING_ELEMENT => $e,
293                         ELEMENTS => [ $e ]}, "x");
294 is($generator->{res}, "if (ndr_flags & NDR_SCALARS) {
295         NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, ndr_string_array_size(ndr, x->el1)));
296         NDR_CHECK(ndr_push_align(ndr, 4));
297         NDR_CHECK(ndr_push_mytype(ndr, NDR_SCALARS, &x->el1));
298 }
299 if (ndr_flags & NDR_BUFFERS) {
300 }
301 ");
302
303 is(TypeFunctionName("ndr_pull", "uint32"), "ndr_pull_uint32");
304 is(TypeFunctionName("ndr_pull", {TYPE => "ENUM", NAME => "bar"}), "ndr_pull_ENUM_bar");
305 is(TypeFunctionName("ndr_pull", {TYPE => "TYPEDEF", NAME => "bar", DATA => undef}), "ndr_pull_bar");
306 is(TypeFunctionName("ndr_push", {TYPE => "STRUCT", NAME => "bar"}), "ndr_push_STRUCT_bar");
307
308 # check noprint works
309 $generator = new Parse::Pidl::Samba4::NDR::Parser();
310 $generator->ParseElementPrint({ NAME => "x", TYPE => "rt", REPRESENTATION_TYPE => "rt", 
311                                     PROPERTIES => { noprint => 1},
312                                     LEVELS => [ { TYPE => "DATA", DATA_TYPE => "rt"} ]}, "var", { "x" => "r->foobar" } );
313 is($generator->{res}, "");
314
315 $generator = new Parse::Pidl::Samba4::NDR::Parser();
316 $generator->ParseElementPrint({ NAME => "x", TYPE => "rt", REPRESENTATION_TYPE => "rt", 
317                                     PROPERTIES => {},
318                                     LEVELS => [ { TYPE => "DATA", DATA_TYPE => "rt" }]}, "var", { "x" => "r->foobar" } );
319 is($generator->{res}, "ndr_print_rt(ndr, \"x\", &var);\n");
320
321 # make sure that a print function for an element with value() set works
322 $generator = new Parse::Pidl::Samba4::NDR::Parser();
323 $generator->ParseElementPrint({ NAME => "x", TYPE => "uint32", REPRESENTATION_TYPE => "uint32", 
324                                     PROPERTIES => { value => "23" },
325                                     LEVELS => [ { TYPE => "DATA", DATA_TYPE => "uint32"} ]}, "var", { "x" => "r->foobar" } );
326 is($generator->{res}, "ndr_print_uint32(ndr, \"x\", (ndr->flags & LIBNDR_PRINT_SET_VALUES)?23:var);\n");