require 'test/unit' require 'protobuf/compiler/compiler' class CompilerTest < Test::Unit::TestCase def save_const(parent_class, const_name) if parent_class.const_defined?(const_name) saved = parent_class.const_get(const_name) parent_class.__send__(:remove_const, const_name) end yield if saved parent_class.__send__(:remove_const, const_name) if parent_class.const_defined?(const_name) parent_class.const_set(const_name, saved) end end # Issue 12: Parse error on float default value def test_compile_float_default assert_compile_proto(<<-EOS, 'test/proto/float_default.proto') ### Generated by rprotoc. DO NOT EDIT! ### # message M { # optional float f = 1 [default = 4.2]; # optional float g = 2 [default = -4.2]; # optional float h = 3 [default = 4352]; # optional float i = 4 [default = 23145.2 ]; # optional float j = 5 [default = -5 ]; # optional float k = 6 [default = +23 ]; # optional float l = 7 [default = +23.42 ]; # } # require 'protobuf/message/message' require 'protobuf/message/enum' require 'protobuf/message/service' require 'protobuf/message/extend' class M < ::Protobuf::Message defined_in __FILE__ optional :float, :f, 1, :default => 4.2 optional :float, :g, 2, :default => -4.2 optional :float, :h, 3, :default => 4352 optional :float, :i, 4, :default => 23145.2 optional :float, :j, 5, :default => -5 optional :float, :k, 6, :default => 23 optional :float, :l, 7, :default => 23.42 end EOS end def test_create_message save_const(Object, :Tutorial) do assert_compile_proto(<<-EOS, 'test/proto/addressbook.proto') ### Generated by rprotoc. DO NOT EDIT! ### # package tutorial; # # message Person { # required string name = 1; # required int32 id = 2; # optional string email = 3; # # enum PhoneType { # MOBILE = 0; # HOME = 1; # WORK = 2; # } # # message PhoneNumber { # required string number = 1; # optional PhoneType type = 2 [default = HOME]; # } # # repeated PhoneNumber phone = 4; # optional uint32 age = 5 [default = 20]; # # extensions 100 to 200; # } # # /* # extend Person { # optional int32 age = 100; # } # */ # # message AddressBook { # repeated Person person = 1; # } require 'protobuf/message/message' require 'protobuf/message/enum' require 'protobuf/message/service' require 'protobuf/message/extend' module Tutorial class Person < ::Protobuf::Message defined_in __FILE__ required :string, :name, 1 required :int32, :id, 2 optional :string, :email, 3 class PhoneType < ::Protobuf::Enum defined_in __FILE__ MOBILE = value(:MOBILE, 0) HOME = value(:HOME, 1) WORK = value(:WORK, 2) end class PhoneNumber < ::Protobuf::Message defined_in __FILE__ required :string, :number, 1 optional :PhoneType, :type, 2, :default => :HOME end repeated :PhoneNumber, :phone, 4 optional :uint32, :age, 5, :default => 20 extensions 100..200 end class AddressBook < ::Protobuf::Message defined_in __FILE__ repeated :Person, :person, 1 end end EOS end end def test_create_nested_message save_const(Test, :Nested) do assert_compile_proto(<<-EOS, 'test/proto/nested.proto') ### Generated by rprotoc. DO NOT EDIT! ### # package test.nested; # # message Foo { # message Bar { # } # } # message Baaz { # optional Foo.Bar x = 1; # } require 'protobuf/message/message' require 'protobuf/message/enum' require 'protobuf/message/service' require 'protobuf/message/extend' module Test module Nested class Foo < ::Protobuf::Message defined_in __FILE__ class Bar < ::Protobuf::Message defined_in __FILE__ end end class Baaz < ::Protobuf::Message defined_in __FILE__ optional :'Foo::Bar', :x, 1 end end end EOS end end def test_nested_message save_const(Test, :Nested) do file_contents = Protobuf::Compiler.new.create_message('test/proto/nested.proto', '.', '.', false) assert_raise(TypeError) {Test::Nested::Baaz.new.x = 1} assert_nothing_raised {Test::Nested::Baaz.new.x = Test::Nested::Foo::Bar.new} end end def test_create_rpc file_contents = Protobuf::Compiler.new.create_rpc('test/proto/rpc.proto', '.', 'test/proto', false) assert_source(<<-EOS, file_contents['test/proto/address_book_service.rb']) require 'protobuf/rpc/server' require 'protobuf/rpc/handler' require 'test/proto/rpc.pb' class Tutorial::SearchHandler < Protobuf::Rpc::Handler request Tutorial::Person response Tutorial::AddressBook def self.process_request(request, response) # TODO: edit this method end end class Tutorial::AddHandler < Protobuf::Rpc::Handler request Tutorial::Person response Tutorial::Person def self.process_request(request, response) # TODO: edit this method end end class Tutorial::AddressBookService < Protobuf::Rpc::Server def setup_handlers @handlers = { :search => Tutorial::SearchHandler, :add => Tutorial::AddHandler, } end end EOS assert_source(<<-EOS, file_contents['test/proto/start_address_book_service']) #!/usr/bin/env ruby require 'address_book_service' Tutorial::AddressBookService.new(:Port => 9999).start EOS assert_source(<<-EOS, file_contents['test/proto/client_search.rb']) #!/usr/bin/env ruby require 'protobuf/rpc/client' require 'test/proto/rpc.pb' # build request request = Tutorial::Person.new # TODO: setup a request raise StandardError, 'setup a request' # create blank response response = Tutorial::AddressBook.new # execute rpc Protobuf::Rpc::Client.new('localhost', 9999).call :search, request, response # show response puts response EOS assert_source(<<-EOS, file_contents['test/proto/client_add.rb']) #!/usr/bin/env ruby require 'protobuf/rpc/client' require 'test/proto/rpc.pb' # build request request = Tutorial::Person.new # TODO: setup a request raise StandardError, 'setup a request' # create blank response response = Tutorial::Person.new # execute rpc Protobuf::Rpc::Client.new('localhost', 9999).call :add, request, response # show response puts response EOS end def test_create_descriptor proto_path = 'test/proto/addressbook.proto' visitor = Protobuf::Visitor::CreateDescriptorVisitor.new(proto_path) File.open(proto_path) do |file| visitor.visit Protobuf::ProtoParser.new.parse(file) end file_descriptor = visitor.file_descriptor assert_equal(proto_path, file_descriptor.name) assert_equal('tutorial', file_descriptor.package) person_descriptor = file_descriptor.message_type[0] assert_equal('Person', person_descriptor.name) assert_equal([:name, :id, :email, :phone, :age].size, person_descriptor.field.size) name_field_descriptor = person_descriptor.field.find {|d| d.name == 'name'} assert_equal(1, name_field_descriptor.number) assert_equal(Google::Protobuf::FieldDescriptorProto::Type::TYPE_STRING, name_field_descriptor.type) assert_equal(Google::Protobuf::FieldDescriptorProto::Label::LABEL_REQUIRED, name_field_descriptor.label) assert_equal('string', name_field_descriptor.type_name) phone_field_descriptor = person_descriptor.field.find {|d| d.name == 'phone'} assert_equal(4, phone_field_descriptor.number) assert_equal(0, phone_field_descriptor.type) #TODO: is this right? assert_equal(Google::Protobuf::FieldDescriptorProto::Label::LABEL_REPEATED, phone_field_descriptor.label) assert_equal('PhoneNumber', phone_field_descriptor.type_name) age_field_descriptor = person_descriptor.field.find {|d| d.name == 'age'} assert_equal(5, age_field_descriptor.number) assert_equal(Google::Protobuf::FieldDescriptorProto::Type::TYPE_UINT32, age_field_descriptor.type) assert_equal(Google::Protobuf::FieldDescriptorProto::Label::LABEL_OPTIONAL, age_field_descriptor.label) assert_equal('uint32', age_field_descriptor.type_name) assert_equal('20', age_field_descriptor.default_value) phone_type_descriptor = person_descriptor.enum_type.first assert_equal('PhoneType', phone_type_descriptor.name) assert_equal(3, phone_type_descriptor.value.size) phone_type_home_descriptor = phone_type_descriptor.value.find {|d| d.name == 'HOME'} assert_equal('HOME', phone_type_home_descriptor.name) assert_equal(1, phone_type_home_descriptor.number) extensions_descriptor = person_descriptor.extension_range.first assert_equal(100, extensions_descriptor.start) assert_equal(200, extensions_descriptor.end) phone_number_descriptor = person_descriptor.nested_type.first assert_equal('PhoneNumber', phone_number_descriptor.name) assert_equal([:number, :type].size, phone_number_descriptor.field.size) #TODO: test extend #extend_person_descriptor = ?? #assert_equal extend_person_descriptor addressbook_descriptor = file_descriptor.message_type[1] assert_equal('AddressBook', addressbook_descriptor.name) end def test_collision assert_raise(Protobuf::TagCollisionError) do require 'test/proto/collision.pb' end assert_raise(Protobuf::TagCollisionError) do Protobuf::Compiler.new.create_message('test/proto/collision.proto', '.', '.', false) end end def test_ext_collision assert_raise(Protobuf::TagCollisionError) do require 'test/proto/ext_collision.pb' end assert_raise(Protobuf::TagCollisionError) do Protobuf::Compiler.new.create_message('test/proto/ext_collision.proto', '.', '.', false) end end def test_ext_range assert_raise(RangeError) do require 'test/proto/ext_range.pb' end assert_raise(RangeError) do Protobuf::Compiler.new.create_message('test/proto/ext_range.proto', '.', '.', false) end end def assert_compile_proto(ideal, filename) assert_equal(ideal.gsub(/^\s*\n/, '').strip, Protobuf::Compiler.new.create_message(filename, '.', '.', false).gsub(/^\s*\n/, '').strip) end def assert_source(ideal, real) assert_equal(ideal.strip.gsub(/^\s*\n/, '').gsub(/\s+\n/, "\n"), real.strip.gsub(/^\s*\n/, '').gsub(/\s+\n/, "\n")) end end