blob: dacaaf59fdebbf315c2ee98220637a9784d8257d [file] [log] [blame]
#
# Copyright 2016 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import json
import os
from unittest import TestCase
import time
from tests.itests.docutests.test_utils import run_command_to_completion_with_raw_stdout
from tests.utests.chameleon.protoc_plugins.test_utils import load_file, \
unindent, save_file
proto_to_yang_cmd='python -m grpc.tools.protoc -I{} ' \
'--plugin=protoc-gen-custom=/voltha/experiments' \
'/proto2yang/proto2yang.py --custom_out={} {}'
yang_validate_cmd="pyang -f tree --ietf {}"
TEMP_PATH="/tmp/proto2yang"
TEMP_INPUT_PROTO_FILE="test.proto"
TEMP_OUTPUT_YANG_FILE="ietf-test.yang"
TEMP_PROTO_PATH='{}/{}'.format(TEMP_PATH, TEMP_INPUT_PROTO_FILE)
TEMP_YANG_PATH='{}/{}'.format(TEMP_PATH, TEMP_OUTPUT_YANG_FILE)
class ProtoToYang(TestCase):
def setup(self):
if not os.path.exists(TEMP_PATH):
os.makedirs(TEMP_PATH)
def _compare_file(self, response, expected_response):
# compare two files and strip empty lines, blanks, etc
def _filter(x):
x.strip()
return x is not None
response = filter(_filter,response.split())
expected_response = filter(_filter,expected_response.split())
print response
print expected_response
self.assertEqual(set(response), set(expected_response))
def _gen_yang(self, proto):
try:
save_file(os.path.join(TEMP_PATH, TEMP_INPUT_PROTO_FILE), proto)
cmd = proto_to_yang_cmd.format(TEMP_PATH, TEMP_PATH, TEMP_PROTO_PATH
)
print 'running command: {}'.format(cmd)
response, err, rc = run_command_to_completion_with_raw_stdout(cmd)
self.assertEqual(rc, 0)
return load_file(TEMP_YANG_PATH)
except Exception as e:
print('Failure to generate yang file {}'.format(e))
def test_01_empty_proto(self):
print "Test_01_empty_proto_Start:------------------"
t0 = time.time()
proto = unindent("""
syntax = "proto3";
package test;
""")
expected_response = """
module ietf-test {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-test";
prefix "voltha";
organization "CORD";
contact
" Any name";
description
"";
revision "2016-11-15" {
description "Initial revision.";
reference "reference";
}
}
"""
try:
yang = self._gen_yang(proto)
self._compare_file(yang, expected_response)
finally:
print "Test_01_empty_proto_End:------------------ took {} " \
"secs\n\n".format(time.time() - t0)
def test_02_empty_message_with_service(self):
print "Test_02_empty_message_with_service_Start:------------------"
t0 = time.time()
proto = unindent("""
syntax = "proto3";
package test;
message Null {}
service TestService {
rpc Get(Null) returns(Null);
}
""")
expected_response = """
module ietf-test {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-test";
prefix "voltha";
organization "CORD";
contact
" Any name";
description
"";
revision "2016-11-15" {
description "Initial revision.";
reference "reference";
}
grouping Null {
description
"";
}
rpc TestService-Get {
description
"";
input {
uses Null;
}
output {
uses Null;
}
}
}
"""
try:
yang = self._gen_yang(proto)
self._compare_file(yang, expected_response)
finally:
print "Test_02_empty_message_with_service_End:------------------ took {} " \
"secs\n\n".format(time.time() - t0)
def test_03_simple_message_with_service(self):
print "Test__03_simple_message_with_service_Start:------------------"
t0 = time.time()
proto = unindent("""
syntax = "proto3";
package test;
// Simple Message
message Simple {
string str = 1; // a string attribute
int32 int = 2; // an int32 attribute
}
// Service to get things done
service TestService {
/* Get simple answer
*
* Returns the true answer to all of life's persistent questions.
*/
rpc Get(Simple) returns(Simple);
}
""")
expected_response = """
module ietf-test {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-test";
prefix "voltha";
organization "CORD";
contact
" Any name";
description
"";
revision "2016-11-15" {
description "Initial revision.";
reference "reference";
}
grouping Simple {
description
"Simple Message";
leaf str {
type string;
description
"a string attribute";
}
leaf int {
type int32;
description
"an int32 attribute";
}
}
/* Service to get things done" */
rpc TestService-Get {
description
"Get simple answer
Returns the true answer to all of life's persistent questions.";
input {
uses Simple;
}
output {
uses Simple;
}
}
}
"""
try:
yang = self._gen_yang(proto)
self._compare_file(yang, expected_response)
finally:
print "Test_03_simple_message_with_service_End" \
":------------------ took {} secs\n\n".format(time.time() - t0)
def test_04_mix_types(self):
print "Test__04_mix_types_Start:------------------"
t0 = time.time()
proto = unindent("""
syntax = "proto3";
package experiment;
message AsyncEvent {
int32 seq = 1;
enum EventType {
BIG_BANG = 0; // just a big bang
SMALL_BANG = 1; // so small bang
NO_BANG = 2;
}
EventType type = 2;
string details = 3;
}
enum SimpleEnum {
APPLE = 0;
BANANA = 1;
ORANGE = 2;
}
message Packet {
int32 source = 1;
bytes content = 2;
message InnerPacket {
string url = 1;
string title = 2;
repeated string snippets = 3;
message InnerInnerPacket {
string input = 1;
string desc = 2;
}
repeated InnerInnerPacket inner_inner_packet = 4;
}
repeated InnerPacket inner_packets = 3;
}
message Echo {
string msg = 1;
float delay = 2;
}
message testMessage{
string test2 = 1;
int32 test3 = 2;
}
service ExperimentalService {
rpc GetEcho(Echo) returns(Echo);
// For server to send async stream to client
rpc ReceiveStreamedEvents(Packet)
returns(stream AsyncEvent);
// For server to send async packets to client
rpc ReceivePackets(Echo) returns(stream Packet);
// For client to send async packets to server
rpc SendPackets(stream Packet) returns(Echo);
}
""")
expected_response = """
module ietf-test {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-test";
prefix "voltha";
organization "CORD";
contact
" Any name";
description
"";
revision "2016-11-15" {
description "Initial revision.";
reference "reference";
}
typedef SimpleEnum {
type enumeration {
enum APPLE {
description "";
}
enum BANANA {
description "";
}
enum ORANGE {
description "";
}
}
description
"";
}
grouping AsyncEvent {
description
"";
leaf seq {
type int32;
description
"";
}
leaf type {
type EventType;
description
"";
}
leaf details {
type string;
description
"";
}
typedef EventType {
type enumeration {
enum BIG_BANG {
description "";
}
enum SMALL_BANG {
description "";
}
enum NO_BANG {
description "";
}
}
description
"";
}
}
grouping Packet {
description
"";
leaf source {
type int32;
description
"";
}
leaf content {
type binary;
description
"";
}
list inner_packets {
key "url";
uses InnerPacket;
description
"";
}
grouping InnerPacket {
description
"";
leaf url {
type string;
description
"";
}
leaf title {
type string;
description
"";
}
list snippets {
key "snippets";
leaf snippets {
type string;
description
"";
}
description
"";
}
list inner_inner_packet {
key "input";
uses InnerInnerPacket;
description
"";
}
grouping InnerInnerPacket {
description
"";
leaf input {
type string;
description
"";
}
leaf desc {
type string;
description
"";
}
}
}
}
grouping Echo {
description
"";
leaf msg {
type string;
description
"";
}
leaf delay {
type decimal64 {
fraction-digits 5;
}
description
"";
}
}
container testMessage {
description
"";
leaf test2 {
type string;
description
"";
}
leaf test3 {
type int32;
description
"";
}
}
rpc ExperimentalService-GetEcho {
description
"";
input {
uses Echo;
}
output {
uses Echo;
}
}
rpc ExperimentalService-ReceiveStreamedEvents {
description
"For server to send async stream to client";
input {
uses Packet;
}
output {
uses AsyncEvent;
}
}
rpc ExperimentalService-ReceivePackets {
description
"For server to send async packets to client";
input {
uses Echo;
}
output {
uses Packet;
}
}
rpc ExperimentalService-SendPackets {
description
"For client to send async packets to server";
input {
uses Packet;
}
output {
uses Echo;
}
}
}
"""
try:
yang = self._gen_yang(proto)
self._compare_file(yang, expected_response)
finally:
print "Test_04_mix_types_End" \
":------------------ took {} secs\n\n".format(time.time() - t0)