vppapigen: add parser support for enumflags
[vpp.git] / src / tools / vppapigen / test_vppapigen.py
1 #!/usr/bin/env python3
2
3 import unittest
4 from vppapigen import VPPAPI, Option, ParseError, Union, foldup_crcs, \
5     global_types
6 import vppapigen
7
8
9 # TODO
10 # - test parsing of options, typedefs, enums, defines
11 # - test JSON, C output
12
13
14 class TestVersion(unittest.TestCase):
15     @classmethod
16     def setUpClass(cls):
17         cls.parser = VPPAPI()
18
19     def test_version(self):
20         version_string = 'option version = "1.0.0";'
21         r = self.parser.parse_string(version_string)
22         self.assertTrue(isinstance(r[0], Option))
23
24
25 class TestUnion(unittest.TestCase):
26     @classmethod
27     def setUpClass(cls):
28         cls.parser = VPPAPI()
29
30     def test_union(self):
31         test_string = '''
32         union foo_union {
33         u32 a;
34         u8 b;
35         };
36         '''
37         r = self.parser.parse_string(test_string)
38         self.assertTrue(isinstance(r[0], Union))
39
40     def test_union_vla(self):
41         test_string = '''
42         union foo_union_vla {
43         u32 a;
44         u8 b[a];
45         };
46         autoreply define foo {
47         vl_api_foo_union_vla_t v;
48         };
49         '''
50         r = self.parser.parse_string(test_string)
51         self.assertTrue(isinstance(r[0], Union))
52         self.assertTrue(r[0].vla)
53         s = self.parser.process(r)
54
55         test_string2 = '''
56         union foo_union_vla2 {
57         u32 a;
58         u8 b[a];
59         u32 c;
60         };
61         autoreply define foo2 {
62         vl_api_foo_union_vla2_t v;
63         };
64         '''
65         self.assertRaises(ValueError, self.parser.parse_string, test_string2)
66
67         test_string3 = '''
68         union foo_union_vla3 {
69         u32 a;
70         u8 b[a];
71         };
72         autoreply define foo3 {
73         vl_api_foo_union_vla3_t v;
74         u32 x;
75         };
76         '''
77         self.assertRaises(ValueError, self.parser.parse_string, test_string3)
78
79
80 class TestTypedef(unittest.TestCase):
81     @classmethod
82     def setUpClass(cls):
83         cls.parser = VPPAPI()
84
85     def test_duplicatetype(self):
86         test_string = '''
87         typedef foo1 { u8 dummy; };
88         typedef foo1 { u8 dummy; };
89         '''
90         self.assertRaises(KeyError, self.parser.parse_string, test_string)
91
92
93 class TestDefine(unittest.TestCase):
94     @classmethod
95     def setUpClass(cls):
96         cls.parser = VPPAPI()
97
98     def test_unknowntype(self):
99         test_string = 'define foo { foobar foo;};'
100         with self.assertRaises(ParseError) as ctx:
101             self.parser.parse_string(test_string)
102         self.assertIn('Undefined type: foobar', str(ctx.exception))
103
104         test_string = 'define { u8 foo;};'
105         with self.assertRaises(ParseError) as ctx:
106             self.parser.parse_string(test_string)
107
108     def test_flags(self):
109         test_string = '''
110           manual_print dont_trace manual_endian define foo { u8 foo; };
111           define foo_reply {u32 context; i32 retval; };
112         '''
113         r = self.parser.parse_string(test_string)
114         self.assertIsNotNone(r)
115         s = self.parser.process(r)
116         self.assertIsNotNone(s)
117         for d in s['Define']:
118             if d.name == 'foo':
119                 self.assertTrue(d.dont_trace)
120                 self.assertTrue(d.manual_endian)
121                 self.assertTrue(d.manual_print)
122                 self.assertFalse(d.autoreply)
123
124         test_string = '''
125           nonexisting_flag define foo { u8 foo; };
126         '''
127         with self.assertRaises(ParseError):
128             self.parser.parse_string(test_string)
129
130     def test_options(self):
131         test_string = '''
132           define foo { option deprecated; u8 foo; };
133           define foo_reply {u32 context; i32 retval; };
134         '''
135         r = self.parser.parse_string(test_string)
136         self.assertIsNotNone(r)
137         s = self.parser.process(r)
138         self.assertIsNotNone(s)
139
140
141 class TestService(unittest.TestCase):
142     @classmethod
143     def setUpClass(cls):
144         cls.parser = VPPAPI()
145
146     def test_service(self):
147         test_string = '''
148          autoreply define show_version { u8 foo;};
149          service { rpc show_version returns show_version_reply; };
150         '''
151         r = self.parser.parse_string(test_string)
152         s = self.parser.process(r)
153         self.assertEqual(s['Service'][0].caller, 'show_version')
154         self.assertEqual(s['Service'][0].reply, 'show_version_reply')
155
156
157 def get_crc(apistring, name):
158     vppapigen.global_types = {}
159     parser = vppapigen.VPPAPI()
160     r = parser.parse_string(apistring)
161     s = parser.process(r)
162     foldup_crcs(s['Define'])
163     d = [f for f in s['Define'] if f.name == name]
164     return d[0].crc
165
166
167 class TestCRC(unittest.TestCase):
168     def test_crc(self):
169         test_string = '''
170          typedef list { u8 foo; };
171          autoreply define foo { u8 foo; vl_api_list_t l;};
172         '''
173         crc = get_crc(test_string, 'foo')
174
175         # modify underlying type
176         test_string = '''
177          typedef list { u8 foo2; };
178          autoreply define foo { u8 foo;  vl_api_list_t l;};
179         '''
180         crc2 = get_crc(test_string, 'foo')
181         self.assertNotEqual(crc, crc2)
182
183         # two user-defined types
184         test_string = '''
185          typedef address { u8 foo2; };
186          typedef list { u8 foo2; vl_api_address_t add; };
187          autoreply define foo { u8 foo;  vl_api_list_t l;};
188         '''
189         crc3 = get_crc(test_string, 'foo')
190
191         test_string = '''
192          typedef address { u8 foo3; };
193          typedef list { u8 foo2; vl_api_address_t add; };
194          autoreply define foo { u8 foo;  vl_api_list_t l;};
195         '''
196         crc4 = get_crc(test_string, 'foo')
197         self.assertNotEqual(crc3, crc4)
198
199         test_string = '''
200          typedef address { u8 foo3; };
201          typedef list { u8 foo2; vl_api_address_t add; u8 foo3; };
202          autoreply define foo { u8 foo;  vl_api_list_t l;};
203         '''
204         crc5 = get_crc(test_string, 'foo')
205         self.assertNotEqual(crc4, crc5)
206
207         test_string = '''
208 typedef ip6_address
209 {
210   u8 foo;
211 };
212 typedef srv6_sid_list
213 {
214   u8 num_sids;
215   u32 weight;
216   u32 sl_index;
217   vl_api_ip6_address_t sids[16];
218 };
219 autoreply define sr_policy_add
220 {
221   u32 client_index;
222   u32 context;
223   vl_api_ip6_address_t bsid_addr;
224   u32 weight;
225   bool is_encap;
226   bool is_spray;
227   u32 fib_table;
228   vl_api_srv6_sid_list_t sids;
229 };
230 '''
231
232         crc = get_crc(test_string, 'sr_policy_add')
233
234         test_string = '''
235 typedef ip6_address
236 {
237   u8 foo;
238 };
239 typedef srv6_sid_list
240 {
241   u8 num_sids;
242   u32 weight;
243   vl_api_ip6_address_t sids[16];
244 };
245 autoreply define sr_policy_add
246 {
247   u32 client_index;
248   u32 context;
249   vl_api_ip6_address_t bsid_addr;
250   u32 weight;
251   bool is_encap;
252   bool is_spray;
253   u32 fib_table;
254   vl_api_srv6_sid_list_t sids;
255 };
256 '''
257         crc2 = get_crc(test_string, 'sr_policy_add')
258
259         self.assertNotEqual(crc, crc2)
260
261
262 class TestEnum(unittest.TestCase):
263
264     @classmethod
265     def setUpClass(cls):
266         cls.parser = VPPAPI()
267
268     def test_enum_as_enum(self):
269         test_string = """\
270 enum tunnel_mode : u8
271 {
272   /** point-to-point */
273   TUNNEL_API_MODE_P2P = 0,
274   /** multi-point */
275   TUNNEL_API_MODE_MP,
276 };
277 """
278         r = self.parser.parse_string(test_string)
279         self.assertIsNotNone(r)
280         s = self.parser.process(r)
281         for o in s['types']:
282             if o.type == 'Enum':
283                 self.assertEqual(o.name, "tunnel_mode")
284                 break
285         else:
286             self.fail()
287
288     def test_enumflag_as_enum(self):
289         test_string = """\
290 enum virtio_flags {
291         VIRTIO_API_FLAG_GSO = 1, /* enable gso on the interface */
292         VIRTIO_API_FLAG_CSUM_OFFLOAD = 2, /* enable checksum offload without gso on the interface */
293         VIRTIO_API_FLAG_GRO_COALESCE = 4, /* enable packet coalescing on tx side, provided gso enabled */
294         VIRTIO_API_FLAG_PACKED = 8, /* enable packed ring support, provided it is available from backend */
295         VIRTIO_API_FLAG_IN_ORDER = 16, /* enable in order support, provided it is available from backend */
296         VIRTIO_API_FLAG_BUFFERING = 32 [backwards_compatible], /* enable buffering to handle backend jitter/delays */
297 };"""
298         r = self.parser.parse_string(test_string)
299         self.assertIsNotNone(r)
300         s = self.parser.process(r)
301         for o in s['types']:
302             if o.type == 'Enum':
303                 self.assertEqual(o.name, "virtio_flags")
304                 break
305         else:
306             self.fail()
307
308
309 class TestEnumFlag(unittest.TestCase):
310
311     @classmethod
312     def setUpClass(cls):
313         cls.parser = VPPAPI()
314
315     def test_enum_as_enumflag(self):
316         test_string = """\
317 enumflag tunnel_mode_ef : u8
318 {
319   /** point-to-point */
320   TUNNEL_API_MODE_P2P = 0,
321   /** multi-point */
322   TUNNEL_API_MODE_MP,
323   TUNNEL_API_MODE_FOO,
324   TUNNEL_API_MODE_BAR,
325 };"""
326         with self.assertRaises(TypeError) as ctx:
327             r = self.parser.parse_string(test_string)
328
329         self.assertTrue(str(ctx.exception).startswith(
330             'tunnel_mode_ef is not a flag enum.'))
331
332     def test_enumflag_as_enumflag(self):
333         test_string = """\
334 enumflag virtio_flags_ef {
335         VIRTIO_API_FLAG_GSO = 1, /* enable gso on the interface */
336         VIRTIO_API_FLAG_CSUM_OFFLOAD = 2, /* enable checksum offload without gso on the interface */
337         VIRTIO_API_FLAG_GRO_COALESCE = 4, /* enable packet coalescing on tx side, provided gso enabled */
338         VIRTIO_API_FLAG_PACKED = 8, /* enable packed ring support, provided it is available from backend */
339         VIRTIO_API_FLAG_IN_ORDER = 16, /* enable in order support, provided it is available from backend */
340         VIRTIO_API_FLAG_BUFFERING = 32 [backwards_compatible], /* enable buffering to handle backend jitter/delays */
341 };"""
342         r = self.parser.parse_string(test_string)
343         self.assertIsNotNone(r)
344         s = self.parser.process(r)
345         for o in s['types']:
346             if o.type == 'EnumFlag':
347                 self.assertEqual(o.name, "virtio_flags_ef")
348                 break
349         else:
350             self.fail()
351
352
353 if __name__ == '__main__':
354     unittest.main(verbosity=2)