blob: 733652416eee6bd645a4af7e8cb447865846f825 [file] [log] [blame]
Khen Nursimulu95b919d2016-11-18 16:20:20 -05001#
2# Copyright 2016 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import json
17import os
18from unittest import TestCase
19import time
20from tests.itests.docutests.test_utils import run_command_to_completion_with_raw_stdout
21from tests.utests.chameleon.protoc_plugins.test_utils import load_file, \
22 unindent, save_file
23
24proto_to_yang_cmd='python -m grpc.tools.protoc -I{} ' \
25 '--plugin=protoc-gen-custom=/voltha/experiments' \
Khen Nursimulu2ef4ce72016-11-23 11:37:38 -050026 '/netconf/proto2yang/proto2yang.py --custom_out={} {}'
Khen Nursimulu95b919d2016-11-18 16:20:20 -050027
28yang_validate_cmd="pyang -f tree --ietf {}"
29
30TEMP_PATH="/tmp/proto2yang"
31TEMP_INPUT_PROTO_FILE="test.proto"
32TEMP_OUTPUT_YANG_FILE="ietf-test.yang"
33TEMP_PROTO_PATH='{}/{}'.format(TEMP_PATH, TEMP_INPUT_PROTO_FILE)
34TEMP_YANG_PATH='{}/{}'.format(TEMP_PATH, TEMP_OUTPUT_YANG_FILE)
35
36
37class ProtoToYang(TestCase):
38
39 def setup(self):
40 if not os.path.exists(TEMP_PATH):
41 os.makedirs(TEMP_PATH)
42
43
44 def _compare_file(self, response, expected_response):
45 # compare two files and strip empty lines, blanks, etc
46 def _filter(x):
47 x.strip()
48 return x is not None
49
50 response = filter(_filter,response.split())
51 expected_response = filter(_filter,expected_response.split())
52 print response
53 print expected_response
54
55 self.assertEqual(set(response), set(expected_response))
56
57 def _gen_yang(self, proto):
58 try:
59 save_file(os.path.join(TEMP_PATH, TEMP_INPUT_PROTO_FILE), proto)
60 cmd = proto_to_yang_cmd.format(TEMP_PATH, TEMP_PATH, TEMP_PROTO_PATH
61 )
62 print 'running command: {}'.format(cmd)
63 response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
64 self.assertEqual(rc, 0)
65
66 return load_file(TEMP_YANG_PATH)
67 except Exception as e:
68 print('Failure to generate yang file {}'.format(e))
69
70
71 def test_01_empty_proto(self):
72 print "Test_01_empty_proto_Start:------------------"
73 t0 = time.time()
74
75 proto = unindent("""
76 syntax = "proto3";
77 package test;
78 """)
79
80 expected_response = """
81 module ietf-test {
82 yang-version 1.1;
83 namespace "urn:ietf:params:xml:ns:yang:ietf-test";
84 prefix "voltha";
85
86 organization "CORD";
87 contact
88 " Any name";
89
90 description
91 "";
92
93 revision "2016-11-15" {
94 description "Initial revision.";
95 reference "reference";
96 }
97 }
98 """
99
100 try:
101 yang = self._gen_yang(proto)
102 self._compare_file(yang, expected_response)
103 finally:
104 print "Test_01_empty_proto_End:------------------ took {} " \
105 "secs\n\n".format(time.time() - t0)
106
107
108 def test_02_empty_message_with_service(self):
109 print "Test_02_empty_message_with_service_Start:------------------"
110 t0 = time.time()
111
112 proto = unindent("""
113 syntax = "proto3";
114 package test;
115 message Null {}
116 service TestService {
117 rpc Get(Null) returns(Null);
118 }
119 """)
120
121 expected_response = """
122 module ietf-test {
123 yang-version 1.1;
124 namespace "urn:ietf:params:xml:ns:yang:ietf-test";
125 prefix "voltha";
126
127 organization "CORD";
128 contact
129 " Any name";
130
131 description
132 "";
133
134 revision "2016-11-15" {
135 description "Initial revision.";
136 reference "reference";
137 }
138
139 grouping Null {
140 description
141 "";
142 }
143
144 rpc TestService-Get {
145 description
146 "";
147 input {
148 uses Null;
149 }
150 output {
151 uses Null;
152 }
153 }
154 }
155 """
156
157 try:
158 yang = self._gen_yang(proto)
159 self._compare_file(yang, expected_response)
160 finally:
161 print "Test_02_empty_message_with_service_End:------------------ took {} " \
162 "secs\n\n".format(time.time() - t0)
163
164
165 def test_03_simple_message_with_service(self):
166 print "Test__03_simple_message_with_service_Start:------------------"
167 t0 = time.time()
168
169 proto = unindent("""
170 syntax = "proto3";
171 package test;
172
173 // Simple Message
174 message Simple {
175 string str = 1; // a string attribute
176 int32 int = 2; // an int32 attribute
177 }
178
179 // Service to get things done
180 service TestService {
181
182 /* Get simple answer
183 *
184 * Returns the true answer to all of life's persistent questions.
185 */
186 rpc Get(Simple) returns(Simple);
187 }
188 """)
189
190 expected_response = """
191 module ietf-test {
192 yang-version 1.1;
193 namespace "urn:ietf:params:xml:ns:yang:ietf-test";
194 prefix "voltha";
195
196 organization "CORD";
197 contact
198 " Any name";
199
200 description
201 "";
202
203 revision "2016-11-15" {
204 description "Initial revision.";
205 reference "reference";
206 }
207
208
209 grouping Simple {
210 description
211 "Simple Message";
212 leaf str {
213 type string;
214 description
215 "a string attribute";
216 }
217
218 leaf int {
219 type int32;
220 description
221 "an int32 attribute";
222 }
223
224 }
225
226 /* Service to get things done" */
227 rpc TestService-Get {
228 description
229 "Get simple answer
230
231 Returns the true answer to all of life's persistent questions.";
232 input {
233 uses Simple;
234 }
235 output {
236 uses Simple;
237 }
238 }
239 }
240 """
241
242 try:
243 yang = self._gen_yang(proto)
244 self._compare_file(yang, expected_response)
245 finally:
246 print "Test_03_simple_message_with_service_End" \
247 ":------------------ took {} secs\n\n".format(time.time() - t0)
248
249
250 def test_04_mix_types(self):
251 print "Test__04_mix_types_Start:------------------"
252 t0 = time.time()
253
254 proto = unindent("""
255 syntax = "proto3";
256
257 package experiment;
258
259 message AsyncEvent {
260 int32 seq = 1;
261 enum EventType {
262 BIG_BANG = 0; // just a big bang
263 SMALL_BANG = 1; // so small bang
264 NO_BANG = 2;
265 }
266 EventType type = 2;
267 string details = 3;
268 }
269
270 enum SimpleEnum {
271 APPLE = 0;
272 BANANA = 1;
273 ORANGE = 2;
274 }
275
276 message Packet {
277 int32 source = 1;
278 bytes content = 2;
279 message InnerPacket {
280 string url = 1;
281 string title = 2;
282 repeated string snippets = 3;
283 message InnerInnerPacket {
284 string input = 1;
285 string desc = 2;
286 }
287 repeated InnerInnerPacket inner_inner_packet = 4;
288 }
289 repeated InnerPacket inner_packets = 3;
290 }
291
292 message Echo {
293 string msg = 1;
294 float delay = 2;
295 }
296
297 message testMessage{
298 string test2 = 1;
299 int32 test3 = 2;
300 }
301
302 service ExperimentalService {
303
304 rpc GetEcho(Echo) returns(Echo);
305
306 // For server to send async stream to client
307 rpc ReceiveStreamedEvents(Packet)
308 returns(stream AsyncEvent);
309
310 // For server to send async packets to client
311 rpc ReceivePackets(Echo) returns(stream Packet);
312
313 // For client to send async packets to server
314 rpc SendPackets(stream Packet) returns(Echo);
315
316 }
317 """)
318
319 expected_response = """
320 module ietf-test {
321 yang-version 1.1;
322 namespace "urn:ietf:params:xml:ns:yang:ietf-test";
323 prefix "voltha";
324
325 organization "CORD";
326 contact
327 " Any name";
328
329 description
330 "";
331
332 revision "2016-11-15" {
333 description "Initial revision.";
334 reference "reference";
335 }
336
337 typedef SimpleEnum {
338 type enumeration {
339 enum APPLE {
340 description "";
341 }
342 enum BANANA {
343 description "";
344 }
345 enum ORANGE {
346 description "";
347 }
348 }
349 description
350 "";
351 }
352
353 grouping AsyncEvent {
354 description
355 "";
356 leaf seq {
357 type int32;
358 description
359 "";
360 }
361
362 leaf type {
363 type EventType;
364 description
365 "";
366 }
367
368 leaf details {
369 type string;
370 description
371 "";
372 }
373
374 typedef EventType {
375 type enumeration {
376 enum BIG_BANG {
377 description "";
378 }
379 enum SMALL_BANG {
380 description "";
381 }
382 enum NO_BANG {
383 description "";
384 }
385 }
386 description
387 "";
388 }
389
390 }
391
392 grouping Packet {
393 description
394 "";
395 leaf source {
396 type int32;
397 description
398 "";
399 }
400
401 leaf content {
402 type binary;
403 description
404 "";
405 }
406
407 list inner_packets {
408 key "url";
409 uses InnerPacket;
410 description
411 "";
412 }
413
414 grouping InnerPacket {
415 description
416 "";
417 leaf url {
418 type string;
419 description
420 "";
421 }
422
423 leaf title {
424 type string;
425 description
426 "";
427 }
428
429 list snippets {
430 key "snippets";
431 leaf snippets {
432 type string;
433 description
434 "";
435 }
436 description
437 "";
438 }
439
440 list inner_inner_packet {
441 key "input";
442 uses InnerInnerPacket;
443 description
444 "";
445 }
446
447 grouping InnerInnerPacket {
448 description
449 "";
450 leaf input {
451 type string;
452 description
453 "";
454 }
455
456 leaf desc {
457 type string;
458 description
459 "";
460 }
461
462 }
463
464 }
465
466 }
467
468 grouping Echo {
469 description
470 "";
471 leaf msg {
472 type string;
473 description
474 "";
475 }
476
477 leaf delay {
478 type decimal64 {
479 fraction-digits 5;
480 }
481 description
482 "";
483 }
484
485 }
486
487 container testMessage {
488 description
489 "";
490 leaf test2 {
491 type string;
492 description
493 "";
494 }
495
496 leaf test3 {
497 type int32;
498 description
499 "";
500 }
501
502 }
503
504 rpc ExperimentalService-GetEcho {
505 description
506 "";
507 input {
508 uses Echo;
509 }
510 output {
511 uses Echo;
512 }
513 }
514
515 rpc ExperimentalService-ReceiveStreamedEvents {
516 description
517 "For server to send async stream to client";
518 input {
519 uses Packet;
520 }
521 output {
522 uses AsyncEvent;
523 }
524 }
525
526 rpc ExperimentalService-ReceivePackets {
527 description
528 "For server to send async packets to client";
529 input {
530 uses Echo;
531 }
532 output {
533 uses Packet;
534 }
535 }
536
537 rpc ExperimentalService-SendPackets {
538 description
539 "For client to send async packets to server";
540 input {
541 uses Packet;
542 }
543 output {
544 uses Echo;
545 }
546 }
547
548
549 }
550 """
551
552 try:
553 yang = self._gen_yang(proto)
554 self._compare_file(yang, expected_response)
555 finally:
556 print "Test_04_mix_types_End" \
557 ":------------------ took {} secs\n\n".format(time.time() - t0)
558