|
|
|
#!/usr/bin/ruby
|
|
|
|
|
|
|
|
# basic_test_pb.rb is in the same directory as this test.
|
|
|
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
|
|
|
|
|
|
|
|
require 'basic_test_pb'
|
|
|
|
require 'common_tests'
|
|
|
|
require 'google/protobuf'
|
|
|
|
require 'json'
|
|
|
|
require 'test/unit'
|
|
|
|
|
|
|
|
module BasicTest
|
|
|
|
TestMessage = BasicTest::TestMessage
|
|
|
|
Outer = BasicTest::Outer
|
|
|
|
|
|
|
|
class MessageContainerTest < Test::Unit::TestCase
|
|
|
|
# Required by CommonTests module to resolve proto3 proto classes used in tests.
|
|
|
|
def proto_module
|
|
|
|
::BasicTest
|
|
|
|
end
|
|
|
|
include CommonTests
|
|
|
|
|
|
|
|
def test_issue_8311_crash
|
|
|
|
BasicTest::Outer8311.new(
|
|
|
|
inners: []
|
|
|
|
)['inners'].to_s
|
|
|
|
|
|
|
|
assert_raises Google::Protobuf::TypeError do
|
|
|
|
BasicTest::Outer8311.new(
|
|
|
|
inners: [nil]
|
|
|
|
).to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_issue_8559_crash
|
|
|
|
msg = TestMessage.new
|
|
|
|
msg.repeated_int32 = ::Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])
|
|
|
|
|
|
|
|
# https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
|
|
|
|
if cruby_or_jruby_9_3_or_higher?
|
|
|
|
GC.start(full_mark: true, immediate_sweep: true)
|
|
|
|
end
|
|
|
|
TestMessage.encode(msg)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_issue_9440
|
|
|
|
msg = HelloRequest.new
|
|
|
|
msg.id = 8
|
|
|
|
assert_equal 8, msg.id
|
|
|
|
msg.version = '1'
|
|
|
|
assert_equal 8, msg.id
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_issue_9507
|
|
|
|
m = BasicTest::NpeMessage.new(
|
|
|
|
other: "foo" # must be set, but can be blank
|
|
|
|
)
|
|
|
|
|
|
|
|
begin
|
|
|
|
encoded = BasicTest::NpeMessage.encode(m)
|
|
|
|
rescue java.lang.NullPointerException
|
|
|
|
flunk "NPE rescued"
|
|
|
|
end
|
|
|
|
decoded = BasicTest::NpeMessage.decode(encoded)
|
|
|
|
decoded.inspect
|
|
|
|
decoded.to_proto
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_has_field
|
|
|
|
m = TestSingularFields.new
|
|
|
|
refute m.has_singular_msg?
|
|
|
|
m.singular_msg = TestMessage2.new
|
|
|
|
assert m.has_singular_msg?
|
|
|
|
assert TestSingularFields.descriptor.lookup('singular_msg').has?(m)
|
|
|
|
|
|
|
|
m = OneofMessage.new
|
|
|
|
refute m.has_my_oneof?
|
|
|
|
refute m.has_a?
|
|
|
|
m.a = "foo"
|
|
|
|
assert m.has_my_oneof?
|
|
|
|
assert m.has_a?
|
|
|
|
assert_true OneofMessage.descriptor.lookup('a').has?(m)
|
|
|
|
|
|
|
|
m = TestSingularFields.new
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
m.has_singular_int32?
|
|
|
|
end
|
|
|
|
assert_raises ArgumentError do
|
|
|
|
TestSingularFields.descriptor.lookup('singular_int32').has?(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
m.has_singular_string?
|
|
|
|
end
|
|
|
|
assert_raises ArgumentError do
|
|
|
|
TestSingularFields.descriptor.lookup('singular_string').has?(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
m.has_singular_bool?
|
|
|
|
end
|
|
|
|
assert_raises ArgumentError do
|
|
|
|
TestSingularFields.descriptor.lookup('singular_bool').has?(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
m = TestMessage.new
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
m.has_repeated_msg?
|
|
|
|
end
|
|
|
|
assert_raises ArgumentError do
|
|
|
|
TestMessage.descriptor.lookup('repeated_msg').has?(m)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_no_presence
|
|
|
|
m = TestSingularFields.new
|
|
|
|
|
|
|
|
# Explicitly setting to zero does not cause anything to be serialized.
|
|
|
|
m.singular_int32 = 0
|
|
|
|
assert_empty TestSingularFields.encode(m)
|
|
|
|
# Explicitly setting to a non-zero value *does* cause serialization.
|
|
|
|
m.singular_int32 = 1
|
|
|
|
refute_empty TestSingularFields.encode(m)
|
|
|
|
m.singular_int32 = 0
|
|
|
|
assert_empty TestSingularFields.encode(m)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_set_clear_defaults
|
|
|
|
m = TestSingularFields.new
|
|
|
|
|
|
|
|
m.singular_int32 = -42
|
|
|
|
assert_equal( -42, m.singular_int32 )
|
|
|
|
m.clear_singular_int32
|
|
|
|
assert_equal 0, m.singular_int32
|
|
|
|
|
|
|
|
m.singular_int32 = 50
|
|
|
|
assert_equal 50, m.singular_int32
|
|
|
|
TestSingularFields.descriptor.lookup('singular_int32').clear(m)
|
|
|
|
assert_equal 0, m.singular_int32
|
|
|
|
|
|
|
|
m.singular_string = "foo bar"
|
|
|
|
assert_equal "foo bar", m.singular_string
|
|
|
|
m.clear_singular_string
|
|
|
|
assert_empty m.singular_string
|
|
|
|
m.singular_string = "foo"
|
|
|
|
assert_equal "foo", m.singular_string
|
|
|
|
TestSingularFields.descriptor.lookup('singular_string').clear(m)
|
|
|
|
assert_empty m.singular_string
|
|
|
|
m.singular_msg = TestMessage2.new(:foo => 42)
|
|
|
|
assert_equal TestMessage2.new(:foo => 42), m.singular_msg
|
|
|
|
assert m.has_singular_msg?
|
|
|
|
m.clear_singular_msg
|
|
|
|
assert_nil m.singular_msg
|
|
|
|
refute m.has_singular_msg?
|
|
|
|
|
|
|
|
m.singular_msg = TestMessage2.new(:foo => 42)
|
|
|
|
assert_equal TestMessage2.new(:foo => 42), m.singular_msg
|
|
|
|
TestSingularFields.descriptor.lookup('singular_msg').clear(m)
|
|
|
|
assert_nil m.singular_msg
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_import_proto2
|
|
|
|
m = TestMessage.new
|
|
|
|
refute m.has_optional_proto2_submessage?
|
|
|
|
m.optional_proto2_submessage = ::FooBar::Proto2::TestImportedMessage.new
|
|
|
|
assert m.has_optional_proto2_submessage?
|
|
|
|
assert TestMessage.descriptor.lookup('optional_proto2_submessage').has?(m)
|
|
|
|
|
|
|
|
m.clear_optional_proto2_submessage
|
|
|
|
refute m.has_optional_proto2_submessage?
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_clear_repeated_fields
|
|
|
|
m = TestMessage.new
|
|
|
|
|
|
|
|
m.repeated_int32.push(1)
|
|
|
|
assert_equal [1], m.repeated_int32
|
|
|
|
m.clear_repeated_int32
|
|
|
|
assert_empty m.repeated_int32
|
|
|
|
m.repeated_int32.push(1)
|
|
|
|
assert_equal [1], m.repeated_int32
|
|
|
|
TestMessage.descriptor.lookup('repeated_int32').clear(m)
|
|
|
|
assert_empty m.repeated_int32
|
|
|
|
m = OneofMessage.new
|
|
|
|
m.a = "foo"
|
|
|
|
assert_equal "foo", m.a
|
|
|
|
assert m.has_my_oneof?
|
|
|
|
assert_equal :a, m.my_oneof
|
|
|
|
m.clear_a
|
|
|
|
refute m.has_my_oneof?
|
|
|
|
|
|
|
|
m.a = "foobar"
|
|
|
|
assert m.has_my_oneof?
|
|
|
|
m.clear_my_oneof
|
|
|
|
refute m.has_my_oneof?
|
|
|
|
|
|
|
|
m.a = "bar"
|
|
|
|
assert_equal "bar", m.a
|
|
|
|
assert m.has_my_oneof?
|
|
|
|
OneofMessage.descriptor.lookup('a').clear(m)
|
|
|
|
refute m.has_my_oneof?
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_initialization_map_errors
|
|
|
|
e = assert_raises ArgumentError do
|
|
|
|
TestMessage.new(:hello => "world")
|
|
|
|
end
|
|
|
|
assert_match(/hello/, e.message)
|
|
|
|
|
|
|
|
e = assert_raises ArgumentError do
|
|
|
|
MapMessage.new(:map_string_int32 => "hello")
|
|
|
|
end
|
|
|
|
assert_equal "Expected Hash object as initializer value for map field 'map_string_int32' (given String).", e.message
|
|
|
|
e = assert_raises ArgumentError do
|
|
|
|
TestMessage.new(:repeated_uint32 => "hello")
|
|
|
|
end
|
|
|
|
assert_equal "Expected array as initializer value for repeated field 'repeated_uint32' (given String).", e.message
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_field
|
|
|
|
m = MapMessage.new
|
|
|
|
assert_empty m.map_string_int32.to_h
|
|
|
|
assert_empty m.map_string_msg.to_h
|
|
|
|
|
|
|
|
m = MapMessage.new(
|
|
|
|
:map_string_int32 => {"a" => 1, "b" => 2},
|
|
|
|
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
|
|
|
|
"b" => TestMessage2.new(:foo => 2)},
|
|
|
|
:map_string_enum => {"a" => :A, "b" => :B})
|
|
|
|
assert_equal ["a", "b"], m.map_string_int32.keys.sort
|
|
|
|
assert_equal 1, m.map_string_int32["a"]
|
|
|
|
assert_equal 2, m.map_string_msg["b"].foo
|
|
|
|
assert_equal :A, m.map_string_enum["a"]
|
|
|
|
m.map_string_int32["c"] = 3
|
|
|
|
assert_equal 3, m.map_string_int32["c"]
|
|
|
|
m.map_string_msg["c"] = TestMessage2.new(:foo => 3)
|
|
|
|
assert_equal TestMessage2.new(:foo => 3), m.map_string_msg["c"]
|
|
|
|
m.map_string_msg.delete("b")
|
|
|
|
m.map_string_msg.delete("c")
|
|
|
|
assert_equal({ "a" => TestMessage2.new(:foo => 1).to_h }, m.map_string_msg.to_h)
|
|
|
|
assert_raises Google::Protobuf::TypeError do
|
|
|
|
m.map_string_msg["e"] = TestMessage.new # wrong value type
|
|
|
|
end
|
|
|
|
# ensure nothing was added by the above
|
|
|
|
assert_equal({ "a" => TestMessage2.new(:foo => 1).to_h }, m.map_string_msg.to_h)
|
|
|
|
m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
|
|
|
|
assert_raises Google::Protobuf::TypeError do
|
|
|
|
m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64)
|
|
|
|
end
|
|
|
|
assert_raises Google::Protobuf::TypeError do
|
|
|
|
m.map_string_int32 = {}
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raises Google::Protobuf::TypeError do
|
|
|
|
m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" })
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_field_with_symbol
|
|
|
|
m = MapMessage.new
|
|
|
|
assert_empty m.map_string_int32.to_h
|
|
|
|
assert_empty m.map_string_msg.to_h
|
|
|
|
|
|
|
|
m = MapMessage.new(
|
|
|
|
:map_string_int32 => {a: 1, "b" => 2},
|
|
|
|
:map_string_msg => {a: TestMessage2.new(:foo => 1),
|
|
|
|
b: TestMessage2.new(:foo => 10)})
|
|
|
|
assert_equal 1, m.map_string_int32[:a]
|
|
|
|
assert_equal 2, m.map_string_int32[:b]
|
|
|
|
assert_equal 10, m.map_string_msg[:b].foo
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_inspect
|
|
|
|
m = MapMessage.new(
|
|
|
|
:map_string_int32 => {"a" => 1, "b" => 2},
|
|
|
|
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
|
|
|
|
"b" => TestMessage2.new(:foo => 2)},
|
|
|
|
:map_string_enum => {"a" => :A, "b" => :B})
|
|
|
|
|
|
|
|
# JRuby doesn't keep consistent ordering so check for either version
|
|
|
|
expected_a = "<BasicTest::MapMessage: map_string_int32: {\"b\"=>2, \"a\"=>1}, map_string_msg: {\"b\"=><BasicTest::TestMessage2: foo: 2>, \"a\"=><BasicTest::TestMessage2: foo: 1>}, map_string_enum: {\"b\"=>:B, \"a\"=>:A}>"
|
|
|
|
expected_b = "<BasicTest::MapMessage: map_string_int32: {\"a\"=>1, \"b\"=>2}, map_string_msg: {\"a\"=><BasicTest::TestMessage2: foo: 1>, \"b\"=><BasicTest::TestMessage2: foo: 2>}, map_string_enum: {\"a\"=>:A, \"b\"=>:B}>"
|
|
|
|
inspect_result = m.inspect
|
|
|
|
assert_includes [expected_a, expected_b], inspect_result
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_corruption
|
|
|
|
# This pattern led to a crash in a previous version of upb/protobuf.
|
|
|
|
m = MapMessage.new(map_string_int32: { "aaa" => 1 })
|
|
|
|
m.map_string_int32['podid'] = 2
|
|
|
|
m.map_string_int32['aaa'] = 3
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_wrappers
|
|
|
|
run_asserts = ->(m) {
|
|
|
|
assert_equal 2.0, m.map_double[0].value
|
|
|
|
assert_equal 4.0, m.map_float[0].value
|
|
|
|
assert_equal 3, m.map_int32[0].value
|
|
|
|
assert_equal 4, m.map_int64[0].value
|
|
|
|
assert_equal 5, m.map_uint32[0].value
|
|
|
|
assert_equal 6, m.map_uint64[0].value
|
|
|
|
assert m.map_bool[0].value
|
|
|
|
assert_equal 'str', m.map_string[0].value
|
|
|
|
assert_equal 'fun', m.map_bytes[0].value
|
|
|
|
}
|
|
|
|
|
|
|
|
m = proto_module::Wrapper.new(
|
|
|
|
map_double: {0 => Google::Protobuf::DoubleValue.new(value: 2.0)},
|
|
|
|
map_float: {0 => Google::Protobuf::FloatValue.new(value: 4.0)},
|
|
|
|
map_int32: {0 => Google::Protobuf::Int32Value.new(value: 3)},
|
|
|
|
map_int64: {0 => Google::Protobuf::Int64Value.new(value: 4)},
|
|
|
|
map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 5)},
|
|
|
|
map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 6)},
|
|
|
|
map_bool: {0 => Google::Protobuf::BoolValue.new(value: true)},
|
|
|
|
map_string: {0 => Google::Protobuf::StringValue.new(value: 'str')},
|
|
|
|
map_bytes: {0 => Google::Protobuf::BytesValue.new(value: 'fun')},
|
|
|
|
)
|
|
|
|
|
|
|
|
run_asserts.call(m)
|
|
|
|
serialized = proto_module::Wrapper::encode(m)
|
|
|
|
m2 = proto_module::Wrapper::decode(serialized)
|
|
|
|
run_asserts.call(m2)
|
|
|
|
|
|
|
|
# Test the case where we are serializing directly from the parsed form
|
|
|
|
# (before anything lazy is materialized).
|
|
|
|
m3 = proto_module::Wrapper::decode(serialized)
|
|
|
|
serialized2 = proto_module::Wrapper::encode(m3)
|
|
|
|
m4 = proto_module::Wrapper::decode(serialized2)
|
|
|
|
run_asserts.call(m4)
|
|
|
|
|
|
|
|
# Test that the lazy form compares equal to the expanded form.
|
|
|
|
m5 = proto_module::Wrapper::decode(serialized2)
|
|
|
|
assert_equal m5, m
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_wrappers_with_default_values
|
|
|
|
run_asserts = ->(m) {
|
|
|
|
assert_equal 0.0, m.map_double[0].value
|
|
|
|
assert_equal 0.0, m.map_float[0].value
|
|
|
|
assert_equal 0, m.map_int32[0].value
|
|
|
|
assert_equal 0, m.map_int64[0].value
|
|
|
|
assert_equal 0, m.map_uint32[0].value
|
|
|
|
assert_equal 0, m.map_uint64[0].value
|
|
|
|
refute m.map_bool[0].value
|
|
|
|
assert_empty m.map_string[0].value
|
|
|
|
assert_empty m.map_bytes[0].value
|
|
|
|
}
|
|
|
|
|
|
|
|
m = proto_module::Wrapper.new(
|
|
|
|
map_double: {0 => Google::Protobuf::DoubleValue.new(value: 0.0)},
|
|
|
|
map_float: {0 => Google::Protobuf::FloatValue.new(value: 0.0)},
|
|
|
|
map_int32: {0 => Google::Protobuf::Int32Value.new(value: 0)},
|
|
|
|
map_int64: {0 => Google::Protobuf::Int64Value.new(value: 0)},
|
|
|
|
map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 0)},
|
|
|
|
map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 0)},
|
|
|
|
map_bool: {0 => Google::Protobuf::BoolValue.new(value: false)},
|
|
|
|
map_string: {0 => Google::Protobuf::StringValue.new(value: '')},
|
|
|
|
map_bytes: {0 => Google::Protobuf::BytesValue.new(value: '')},
|
|
|
|
)
|
|
|
|
|
|
|
|
run_asserts.call(m)
|
|
|
|
serialized = proto_module::Wrapper::encode(m)
|
|
|
|
m2 = proto_module::Wrapper::decode(serialized)
|
|
|
|
run_asserts.call(m2)
|
|
|
|
|
|
|
|
# Test the case where we are serializing directly from the parsed form
|
|
|
|
# (before anything lazy is materialized).
|
|
|
|
m3 = proto_module::Wrapper::decode(serialized)
|
|
|
|
serialized2 = proto_module::Wrapper::encode(m3)
|
|
|
|
m4 = proto_module::Wrapper::decode(serialized2)
|
|
|
|
run_asserts.call(m4)
|
|
|
|
|
|
|
|
# Test that the lazy form compares equal to the expanded form.
|
|
|
|
m5 = proto_module::Wrapper::decode(serialized2)
|
|
|
|
assert_equal m5, m
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_wrappers_with_no_value
|
|
|
|
run_asserts = ->(m) {
|
|
|
|
assert_equal 0.0, m.map_double[0].value
|
|
|
|
assert_equal 0.0, m.map_float[0].value
|
|
|
|
assert_equal 0, m.map_int32[0].value
|
|
|
|
assert_equal 0, m.map_int64[0].value
|
|
|
|
assert_equal 0, m.map_uint32[0].value
|
|
|
|
assert_equal 0, m.map_uint64[0].value
|
|
|
|
refute m.map_bool[0].value
|
|
|
|
assert_empty m.map_string[0].value
|
|
|
|
assert_empty m.map_bytes[0].value
|
|
|
|
}
|
|
|
|
|
|
|
|
m = proto_module::Wrapper.new(
|
|
|
|
map_double: {0 => Google::Protobuf::DoubleValue.new()},
|
|
|
|
map_float: {0 => Google::Protobuf::FloatValue.new()},
|
|
|
|
map_int32: {0 => Google::Protobuf::Int32Value.new()},
|
|
|
|
map_int64: {0 => Google::Protobuf::Int64Value.new()},
|
|
|
|
map_uint32: {0 => Google::Protobuf::UInt32Value.new()},
|
|
|
|
map_uint64: {0 => Google::Protobuf::UInt64Value.new()},
|
|
|
|
map_bool: {0 => Google::Protobuf::BoolValue.new()},
|
|
|
|
map_string: {0 => Google::Protobuf::StringValue.new()},
|
|
|
|
map_bytes: {0 => Google::Protobuf::BytesValue.new()},
|
|
|
|
)
|
|
|
|
run_asserts.call(m)
|
|
|
|
|
|
|
|
serialized = proto_module::Wrapper::encode(m)
|
|
|
|
m2 = proto_module::Wrapper::decode(serialized)
|
|
|
|
run_asserts.call(m2)
|
|
|
|
|
|
|
|
# Test the case where we are serializing directly from the parsed form
|
|
|
|
# (before anything lazy is materialized).
|
|
|
|
m3 = proto_module::Wrapper::decode(serialized)
|
|
|
|
serialized2 = proto_module::Wrapper::encode(m3)
|
|
|
|
m4 = proto_module::Wrapper::decode(serialized2)
|
|
|
|
run_asserts.call(m4)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_concurrent_decoding
|
|
|
|
o = Outer.new
|
|
|
|
o.items[0] = Inner.new
|
|
|
|
raw = Outer.encode(o)
|
|
|
|
|
|
|
|
thds = 2.times.map do
|
|
|
|
Thread.new do
|
|
|
|
100000.times do
|
|
|
|
assert_equal o, Outer.decode(raw)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
thds.map(&:join)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_encode_decode
|
|
|
|
m = MapMessage.new(
|
|
|
|
:map_string_int32 => {"a" => 1, "b" => 2},
|
|
|
|
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
|
|
|
|
"b" => TestMessage2.new(:foo => 2)},
|
|
|
|
:map_string_enum => {"a" => :A, "b" => :B})
|
|
|
|
m2 = MapMessage.decode(MapMessage.encode(m))
|
|
|
|
assert_equal m, m2
|
|
|
|
m3 = MapMessageWireEquiv.decode(MapMessage.encode(m))
|
|
|
|
assert_equal 2, m3.map_string_int32.length
|
|
|
|
kv = {}
|
|
|
|
m3.map_string_int32.map { |msg| kv[msg.key] = msg.value }
|
|
|
|
assert_equal({"a" => 1, "b" => 2}, kv)
|
|
|
|
kv = {}
|
|
|
|
m3.map_string_msg.map { |msg| kv[msg.key] = msg.value }
|
|
|
|
assert_equal({"a" => TestMessage2.new(:foo => 1),
|
|
|
|
"b" => TestMessage2.new(:foo => 2)}, kv)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_protobuf_decode_json_ignore_unknown_fields
|
|
|
|
m = TestMessage.decode_json({
|
|
|
|
optional_string: "foo",
|
|
|
|
not_in_message: "some_value"
|
|
|
|
}.to_json, { ignore_unknown_fields: true })
|
|
|
|
|
|
|
|
assert_equal "foo", m.optional_string
|
|
|
|
e = assert_raises Google::Protobuf::ParseError do
|
|
|
|
TestMessage.decode_json({ not_in_message: "some_value" }.to_json)
|
|
|
|
end
|
|
|
|
assert_match(/No such field: not_in_message/, e.message)
|
|
|
|
end
|
|
|
|
|
|
|
|
#def test_json_quoted_string
|
|
|
|
# m = TestMessage.decode_json(%q(
|
|
|
|
# "optionalInt64": "1",,
|
|
|
|
# }))
|
|
|
|
# puts(m)
|
|
|
|
# assert_equal 1, m.optional_int32
|
|
|
|
#end
|
|
|
|
|
|
|
|
def test_to_h
|
|
|
|
m = TestMessage.new(
|
|
|
|
:optional_bool => true,
|
|
|
|
:optional_double => -10.100001,
|
|
|
|
:optional_string => 'foo',
|
|
|
|
:repeated_string => ['bar1', 'bar2'],
|
|
|
|
:repeated_msg => [TestMessage2.new(:foo => 100)]
|
|
|
|
)
|
|
|
|
expected_result = {
|
|
|
|
:optional_bool=>true,
|
|
|
|
:optional_double=>-10.100001,
|
|
|
|
:optional_string=>"foo",
|
|
|
|
:repeated_string=>["bar1", "bar2"],
|
|
|
|
:repeated_msg=>[{:foo => 100}],
|
|
|
|
}
|
|
|
|
assert_equal expected_result, m.to_h
|
|
|
|
|
|
|
|
m = MapMessage.new(
|
|
|
|
:map_string_int32 => {"a" => 1, "b" => 2},
|
|
|
|
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
|
|
|
|
"b" => TestMessage2.new(:foo => 2)},
|
|
|
|
:map_string_enum => {"a" => :A, "b" => :B})
|
|
|
|
expected_result = {
|
|
|
|
:map_string_int32 => {"a" => 1, "b" => 2},
|
|
|
|
:map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}},
|
|
|
|
:map_string_enum => {"a" => :A, "b" => :B}
|
|
|
|
}
|
|
|
|
assert_equal expected_result, m.to_h
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def test_json_maps
|
|
|
|
m = MapMessage.new(:map_string_int32 => {"a" => 1})
|
|
|
|
expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}}
|
|
|
|
expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}}
|
|
|
|
assert_equal expected, JSON.parse(MapMessage.encode_json(m, :emit_defaults=>true), :symbolize_names => true)
|
|
|
|
|
Ported Ruby extension to upb_msg (#8184)
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* Added some missing files.
* WIP.
* WIP.
* Updated upb.
* Extension loads, but crashes immediately.
* Gets through the test suite without SEGV!
Still a lot of bugs to fix, but it is a major step!
214 tests, 378 assertions, 37 failures, 147 errors, 0 pendings, 0 omissions, 0 notifications
14.0187% passed
* Test and build for Ruby 3.0
* Fixed a few more bugs, efficient #inspect is almost done.
214 tests, 134243 assertions, 30 failures, 144 errors, 0 pendings, 0 omissions, 0 notifications
18.6916% passed
* Fixed message hash initialization and encode depth checking.
214 tests, 124651 assertions, 53 failures, 70 errors, 0 pendings, 0 omissions, 0 notifications
42.5234% passed
* A bunch of fixes to failing tests, now 70% passing.
214 tests, 202091 assertions, 41 failures, 23 errors, 0 pendings, 0 omissions, 0 notifications
70.0935% passed
* More than 80% of tests are passing now.
214 tests, 322331 assertions, 30 failures, 9 errors, 0 pendings, 0 omissions, 0 notifications
81.7757% passed
Unfortunately there is also a sporadic bug/segfault hanging around
that appears to be GC-related.
* Add linux/ruby30 and macos/ruby30
* Use rvm master for 3.0.0-preview2
* Over 90% of tests are passing!
214 tests, 349898 assertions, 15 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
92.5234% passed
* Passes all tests!
214 tests, 369388 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
* A bunch of cleanup.
1. Removed a bunch of internal-only symbols from headers.
2. Required a frozen check to get a non-const pointer to a map or array.
3. De-duplicated the code to get a type argument for Map/RepeatedField.
* Removed a bunch more stuff from protobuf.h. There is an intermittent assert failure.
Intermittent failure:
ruby: ../../../../ext/google/protobuf_c/protobuf.c:263: ObjectCache_Add: Assertion `rb_funcall(obj_cache2, (__builtin_constant_p("[]") ? __extension__ ({ static ID rb_intern_id_cache; if (!rb_intern_id_cache) rb_intern_id_cache = rb_intern2((("[]")
), (long)strlen(("[]"))); (ID) rb_intern_id_cache; }) : rb_intern("[]")), 1, key_rb) == val' failed
* Removed a few more things from protobuf.h.
* Ruby 3.0.0-preview2 to 3.0.0
* Require rake-compiler-dock >= 1.1.0
* More progress, fighting with the object cache.
* Passes on all Ruby versions!
* Updated and clarified comment regarding WeakMap.
* Fixed the wyhash compile.
* Fixed conformance tests for Ruby.
Conformance results now look like:
RUBYLIB=../ruby/lib:. ./conformance-test-runner --enforce_recommended --failure_list failure_list_ruby.txt --text_format_failure_list text_format_failure_list_ruby.txt ./conformance_ruby.rb
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 1955 successes, 0 skipped, 58 expected failures, 0 unexpected failures.
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 0 successes, 111 skipped, 8 expected failures, 0 unexpected failures.
Fixes include:
- Changed Ruby compiler to no longer reject proto2 maps.
- Changed Ruby compiler to emit a warning when proto2 extensions are
present instead of rejecting the .proto file completely.
- Fixed conformance tests to allow proto2 and look up message by name
instead of hardcoding a specific list of messages.
- Fixed conformance test to support the "ignore unknown" option for
JSON.
- Fixed conformance test to properly report serialization errors.
* Removed debug printf and fixed #inspect for floats.
* Fixed compatibility test to have proper semantics for #to_json.
* Updated Makefile.am with new file list.
* Don't try to copy wyhash when inside Docker.
* Fixed bug where we would forget that a sub-object is frozen in Ruby >=2.7.
* Avoid exporting unneeded symbols and refactored a bit of code.
* Some more refactoring.
* Simplified and added more comments.
* Some more comments and simplification. Added a missing license block.
Co-authored-by: Masaki Hara <hara@wantedly.com>
4 years ago
|
|
|
json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true, :emit_defaults=>true)
|
|
|
|
assert_equal expected_preserve, JSON.parse(json, :symbolize_names => true)
|
|
|
|
|
|
|
|
m2 = MapMessage.decode_json(MapMessage.encode_json(m))
|
|
|
|
assert_equal m, m2
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_json_maps_emit_defaults_submsg
|
Ported Ruby extension to upb_msg (#8184)
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* Added some missing files.
* WIP.
* WIP.
* Updated upb.
* Extension loads, but crashes immediately.
* Gets through the test suite without SEGV!
Still a lot of bugs to fix, but it is a major step!
214 tests, 378 assertions, 37 failures, 147 errors, 0 pendings, 0 omissions, 0 notifications
14.0187% passed
* Test and build for Ruby 3.0
* Fixed a few more bugs, efficient #inspect is almost done.
214 tests, 134243 assertions, 30 failures, 144 errors, 0 pendings, 0 omissions, 0 notifications
18.6916% passed
* Fixed message hash initialization and encode depth checking.
214 tests, 124651 assertions, 53 failures, 70 errors, 0 pendings, 0 omissions, 0 notifications
42.5234% passed
* A bunch of fixes to failing tests, now 70% passing.
214 tests, 202091 assertions, 41 failures, 23 errors, 0 pendings, 0 omissions, 0 notifications
70.0935% passed
* More than 80% of tests are passing now.
214 tests, 322331 assertions, 30 failures, 9 errors, 0 pendings, 0 omissions, 0 notifications
81.7757% passed
Unfortunately there is also a sporadic bug/segfault hanging around
that appears to be GC-related.
* Add linux/ruby30 and macos/ruby30
* Use rvm master for 3.0.0-preview2
* Over 90% of tests are passing!
214 tests, 349898 assertions, 15 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
92.5234% passed
* Passes all tests!
214 tests, 369388 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
* A bunch of cleanup.
1. Removed a bunch of internal-only symbols from headers.
2. Required a frozen check to get a non-const pointer to a map or array.
3. De-duplicated the code to get a type argument for Map/RepeatedField.
* Removed a bunch more stuff from protobuf.h. There is an intermittent assert failure.
Intermittent failure:
ruby: ../../../../ext/google/protobuf_c/protobuf.c:263: ObjectCache_Add: Assertion `rb_funcall(obj_cache2, (__builtin_constant_p("[]") ? __extension__ ({ static ID rb_intern_id_cache; if (!rb_intern_id_cache) rb_intern_id_cache = rb_intern2((("[]")
), (long)strlen(("[]"))); (ID) rb_intern_id_cache; }) : rb_intern("[]")), 1, key_rb) == val' failed
* Removed a few more things from protobuf.h.
* Ruby 3.0.0-preview2 to 3.0.0
* Require rake-compiler-dock >= 1.1.0
* More progress, fighting with the object cache.
* Passes on all Ruby versions!
* Updated and clarified comment regarding WeakMap.
* Fixed the wyhash compile.
* Fixed conformance tests for Ruby.
Conformance results now look like:
RUBYLIB=../ruby/lib:. ./conformance-test-runner --enforce_recommended --failure_list failure_list_ruby.txt --text_format_failure_list text_format_failure_list_ruby.txt ./conformance_ruby.rb
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 1955 successes, 0 skipped, 58 expected failures, 0 unexpected failures.
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 0 successes, 111 skipped, 8 expected failures, 0 unexpected failures.
Fixes include:
- Changed Ruby compiler to no longer reject proto2 maps.
- Changed Ruby compiler to emit a warning when proto2 extensions are
present instead of rejecting the .proto file completely.
- Fixed conformance tests to allow proto2 and look up message by name
instead of hardcoding a specific list of messages.
- Fixed conformance test to support the "ignore unknown" option for
JSON.
- Fixed conformance test to properly report serialization errors.
* Removed debug printf and fixed #inspect for floats.
* Fixed compatibility test to have proper semantics for #to_json.
* Updated Makefile.am with new file list.
* Don't try to copy wyhash when inside Docker.
* Fixed bug where we would forget that a sub-object is frozen in Ruby >=2.7.
* Avoid exporting unneeded symbols and refactored a bit of code.
* Some more refactoring.
* Simplified and added more comments.
* Some more comments and simplification. Added a missing license block.
Co-authored-by: Masaki Hara <hara@wantedly.com>
4 years ago
|
|
|
m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new(foo: 0)})
|
|
|
|
expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}}
|
|
|
|
|
|
|
|
actual = MapMessage.encode_json(m, :emit_defaults => true)
|
|
|
|
|
|
|
|
assert_equal expected, JSON.parse(actual, :symbolize_names => true)
|
|
|
|
end
|
|
|
|
|
Ported Ruby extension to upb_msg (#8184)
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* WIP.
* Added some missing files.
* WIP.
* WIP.
* Updated upb.
* Extension loads, but crashes immediately.
* Gets through the test suite without SEGV!
Still a lot of bugs to fix, but it is a major step!
214 tests, 378 assertions, 37 failures, 147 errors, 0 pendings, 0 omissions, 0 notifications
14.0187% passed
* Test and build for Ruby 3.0
* Fixed a few more bugs, efficient #inspect is almost done.
214 tests, 134243 assertions, 30 failures, 144 errors, 0 pendings, 0 omissions, 0 notifications
18.6916% passed
* Fixed message hash initialization and encode depth checking.
214 tests, 124651 assertions, 53 failures, 70 errors, 0 pendings, 0 omissions, 0 notifications
42.5234% passed
* A bunch of fixes to failing tests, now 70% passing.
214 tests, 202091 assertions, 41 failures, 23 errors, 0 pendings, 0 omissions, 0 notifications
70.0935% passed
* More than 80% of tests are passing now.
214 tests, 322331 assertions, 30 failures, 9 errors, 0 pendings, 0 omissions, 0 notifications
81.7757% passed
Unfortunately there is also a sporadic bug/segfault hanging around
that appears to be GC-related.
* Add linux/ruby30 and macos/ruby30
* Use rvm master for 3.0.0-preview2
* Over 90% of tests are passing!
214 tests, 349898 assertions, 15 failures, 1 errors, 0 pendings, 0 omissions, 0 notifications
92.5234% passed
* Passes all tests!
214 tests, 369388 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
* A bunch of cleanup.
1. Removed a bunch of internal-only symbols from headers.
2. Required a frozen check to get a non-const pointer to a map or array.
3. De-duplicated the code to get a type argument for Map/RepeatedField.
* Removed a bunch more stuff from protobuf.h. There is an intermittent assert failure.
Intermittent failure:
ruby: ../../../../ext/google/protobuf_c/protobuf.c:263: ObjectCache_Add: Assertion `rb_funcall(obj_cache2, (__builtin_constant_p("[]") ? __extension__ ({ static ID rb_intern_id_cache; if (!rb_intern_id_cache) rb_intern_id_cache = rb_intern2((("[]")
), (long)strlen(("[]"))); (ID) rb_intern_id_cache; }) : rb_intern("[]")), 1, key_rb) == val' failed
* Removed a few more things from protobuf.h.
* Ruby 3.0.0-preview2 to 3.0.0
* Require rake-compiler-dock >= 1.1.0
* More progress, fighting with the object cache.
* Passes on all Ruby versions!
* Updated and clarified comment regarding WeakMap.
* Fixed the wyhash compile.
* Fixed conformance tests for Ruby.
Conformance results now look like:
RUBYLIB=../ruby/lib:. ./conformance-test-runner --enforce_recommended --failure_list failure_list_ruby.txt --text_format_failure_list text_format_failure_list_ruby.txt ./conformance_ruby.rb
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 1955 successes, 0 skipped, 58 expected failures, 0 unexpected failures.
CONFORMANCE TEST BEGIN ====================================
CONFORMANCE SUITE PASSED: 0 successes, 111 skipped, 8 expected failures, 0 unexpected failures.
Fixes include:
- Changed Ruby compiler to no longer reject proto2 maps.
- Changed Ruby compiler to emit a warning when proto2 extensions are
present instead of rejecting the .proto file completely.
- Fixed conformance tests to allow proto2 and look up message by name
instead of hardcoding a specific list of messages.
- Fixed conformance test to support the "ignore unknown" option for
JSON.
- Fixed conformance test to properly report serialization errors.
* Removed debug printf and fixed #inspect for floats.
* Fixed compatibility test to have proper semantics for #to_json.
* Updated Makefile.am with new file list.
* Don't try to copy wyhash when inside Docker.
* Fixed bug where we would forget that a sub-object is frozen in Ruby >=2.7.
* Avoid exporting unneeded symbols and refactored a bit of code.
* Some more refactoring.
* Simplified and added more comments.
* Some more comments and simplification. Added a missing license block.
Co-authored-by: Masaki Hara <hara@wantedly.com>
4 years ago
|
|
|
def test_json_emit_defaults_submsg
|
|
|
|
m = TestSingularFields.new(singular_msg: proto_module::TestMessage2.new)
|
|
|
|
|
|
|
|
expected = {
|
|
|
|
singularInt32: 0,
|
|
|
|
singularInt64: "0",
|
|
|
|
singularUint32: 0,
|
|
|
|
singularUint64: "0",
|
|
|
|
singularBool: false,
|
|
|
|
singularFloat: 0,
|
|
|
|
singularDouble: 0,
|
|
|
|
singularString: "",
|
|
|
|
singularBytes: "",
|
|
|
|
singularMsg: {},
|
|
|
|
singularEnum: "Default",
|
|
|
|
}
|
|
|
|
|
|
|
|
actual = proto_module::TestMessage.encode_json(m, :emit_defaults => true)
|
|
|
|
|
|
|
|
assert_equal expected, JSON.parse(actual, :symbolize_names => true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_respond_to
|
|
|
|
msg = MapMessage.new
|
|
|
|
assert_respond_to msg, :map_string_int32
|
|
|
|
refute_respond_to msg, :bacon
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_file_descriptor
|
|
|
|
file_descriptor = TestMessage.descriptor.file_descriptor
|
|
|
|
refute_nil file_descriptor
|
|
|
|
assert_equal "basic_test.proto", file_descriptor.name
|
|
|
|
|
|
|
|
file_descriptor = TestEnum.descriptor.file_descriptor
|
|
|
|
refute_nil file_descriptor
|
|
|
|
assert_equal "basic_test.proto", file_descriptor.name
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_freeze
|
|
|
|
m = proto_module::MapMessage.new
|
|
|
|
m.map_string_int32['a'] = 5
|
|
|
|
m.map_string_msg['b'] = proto_module::TestMessage2.new
|
|
|
|
|
|
|
|
m.map_string_int32.freeze
|
|
|
|
m.map_string_msg.freeze
|
|
|
|
|
|
|
|
assert m.map_string_int32.frozen?
|
|
|
|
assert m.map_string_msg.frozen?
|
|
|
|
|
|
|
|
assert_raises(FrozenError) { m.map_string_int32['foo'] = 1 }
|
|
|
|
assert_raises(FrozenError) { m.map_string_msg['bar'] = proto_module::TestMessage2.new }
|
|
|
|
assert_raises(FrozenError) { m.map_string_int32.delete('a') }
|
|
|
|
assert_raises(FrozenError) { m.map_string_int32.clear }
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_length
|
|
|
|
m = proto_module::MapMessage.new
|
|
|
|
assert_equal 0, m.map_string_int32.length
|
|
|
|
assert_equal 0, m.map_string_msg.length
|
|
|
|
assert_equal 0, m.map_string_int32.size
|
|
|
|
assert_equal 0, m.map_string_msg.size
|
|
|
|
|
|
|
|
m.map_string_int32['a'] = 1
|
|
|
|
m.map_string_int32['b'] = 2
|
|
|
|
m.map_string_msg['a'] = proto_module::TestMessage2.new
|
|
|
|
assert_equal 2, m.map_string_int32.length
|
|
|
|
assert_equal 1, m.map_string_msg.length
|
|
|
|
assert_equal 2, m.map_string_int32.size
|
|
|
|
assert_equal 1, m.map_string_msg.size
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_string_with_singleton_class_enabled
|
|
|
|
str = 'foobar'
|
|
|
|
# NOTE: Accessing a singleton class of an object changes its low level class representation
|
|
|
|
# as far as the C API's CLASS_OF() method concerned, exposing the issue
|
|
|
|
str.singleton_class
|
|
|
|
m = proto_module::TestMessage.new(
|
|
|
|
optional_string: str,
|
|
|
|
optional_bytes: str
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_equal str, m.optional_string
|
|
|
|
assert_equal str, m.optional_bytes
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_utf8
|
|
|
|
m = proto_module::TestMessage.new(
|
|
|
|
optional_string: "µpb",
|
|
|
|
)
|
|
|
|
m2 = proto_module::TestMessage.decode(proto_module::TestMessage.encode(m))
|
|
|
|
assert_equal m2, m
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_map_fields_respond_to? # regression test for issue 9202
|
|
|
|
msg = proto_module::MapMessage.new
|
|
|
|
assert_respond_to msg, :map_string_int32=
|
|
|
|
msg.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
|
|
|
|
assert_respond_to msg, :map_string_int32
|
|
|
|
assert_equal( Google::Protobuf::Map.new(:string, :int32), msg.map_string_int32 )
|
|
|
|
assert_respond_to msg, :clear_map_string_int32
|
|
|
|
msg.clear_map_string_int32
|
|
|
|
|
|
|
|
refute_respond_to msg, :has_map_string_int32?
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
msg.has_map_string_int32?
|
|
|
|
end
|
|
|
|
refute_respond_to msg, :map_string_int32_as_value
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
msg.map_string_int32_as_value
|
|
|
|
end
|
|
|
|
refute_respond_to msg, :map_string_int32_as_value=
|
|
|
|
assert_raises NoMethodError do
|
|
|
|
msg.map_string_int32_as_value = :boom
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_has_presence
|
|
|
|
assert_true TestMessage.descriptor.lookup("optional_int32").has_presence?
|
|
|
|
assert_false TestMessage.descriptor.lookup("repeated_int32").has_presence?
|
|
|
|
assert_false TestSingularFields.descriptor.lookup("singular_int32").has_presence?
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_is_packed
|
|
|
|
assert_false TestMessage.descriptor.lookup("optional_int32").is_packed?
|
|
|
|
assert_true TestMessage.descriptor.lookup("repeated_int32").is_packed?
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_file_descriptor_options
|
|
|
|
file_descriptor = TestMessage.descriptor.file_descriptor
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::FileOptions, file_descriptor.options
|
|
|
|
assert file_descriptor.options.deprecated
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_field_descriptor_options
|
|
|
|
field_descriptor = TestDeprecatedMessage.descriptor.lookup("foo")
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::FieldOptions, field_descriptor.options
|
|
|
|
assert field_descriptor.options.deprecated
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_descriptor_options
|
|
|
|
descriptor = TestDeprecatedMessage.descriptor
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::MessageOptions, descriptor.options
|
|
|
|
assert descriptor.options.deprecated
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_enum_descriptor_options
|
|
|
|
enum_descriptor = TestDeprecatedEnum.descriptor
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::EnumOptions, enum_descriptor.options
|
|
|
|
assert enum_descriptor.options.deprecated
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_oneof_descriptor_options
|
|
|
|
descriptor = TestDeprecatedMessage.descriptor
|
|
|
|
oneof_descriptor = descriptor.lookup_oneof("test_deprecated_message_oneof")
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::OneofOptions, oneof_descriptor.options
|
|
|
|
test_top_level_option = Google::Protobuf::DescriptorPool.generated_pool.lookup 'basic_test.test_top_level_option'
|
|
|
|
assert_instance_of Google::Protobuf::FieldDescriptor, test_top_level_option
|
|
|
|
assert_equal "Custom option value", test_top_level_option.get(oneof_descriptor.options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_nested_extension
|
|
|
|
descriptor = TestDeprecatedMessage.descriptor
|
|
|
|
oneof_descriptor = descriptor.lookup_oneof("test_deprecated_message_oneof")
|
|
|
|
|
|
|
|
assert_instance_of Google::Protobuf::OneofOptions, oneof_descriptor.options
|
|
|
|
test_nested_option = Google::Protobuf::DescriptorPool.generated_pool.lookup 'basic_test.TestDeprecatedMessage.test_nested_option'
|
|
|
|
assert_instance_of Google::Protobuf::FieldDescriptor, test_nested_option
|
|
|
|
assert_equal "Another custom option value", test_nested_option.get(oneof_descriptor.options)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_options_deep_freeze
|
|
|
|
descriptor = TestDeprecatedMessage.descriptor
|
|
|
|
|
|
|
|
assert_raise FrozenError do
|
|
|
|
descriptor.options.uninterpreted_option.push \
|
|
|
|
Google::Protobuf::UninterpretedOption.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_message_freeze
|
|
|
|
message = TestDeprecatedMessage.new
|
|
|
|
nested_message_2 = TestMessage2.new
|
|
|
|
|
|
|
|
message.map_string_msg["message"] = TestMessage2.new
|
|
|
|
message.repeated_msg.push(TestMessage2.new)
|
|
|
|
|
|
|
|
message.freeze
|
|
|
|
|
|
|
|
assert_raise FrozenError do
|
|
|
|
message.map_string_msg["message"].foo = "bar"
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise FrozenError do
|
|
|
|
message.repeated_msg[0].foo = "bar"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_oneof_fields_respond_to? # regression test for issue 9202
|
|
|
|
msg = proto_module::OneofMessage.new
|
|
|
|
# `has_` prefix + "?" suffix actions should work for oneofs fields and members.
|
|
|
|
assert msg.has_my_oneof?
|
|
|
|
assert msg.respond_to? :has_my_oneof?
|
|
|
|
assert_respond_to msg, :has_a?
|
|
|
|
refute msg.has_a?
|
|
|
|
assert_respond_to msg, :has_b?
|
|
|
|
refute msg.has_b?
|
|
|
|
assert_respond_to msg, :has_c?
|
|
|
|
refute msg.has_c?
|
|
|
|
assert_respond_to msg, :has_d?
|
|
|
|
refute msg.has_d?
|
|
|
|
end
|
Fix TypeError when passing an instance of a subclass of String to a string field (#13818)
## Issue
When an object that is an instance of a string-derived class is passed to a string field in a protobuf message in Ruby, it results in a `Google::Protobuf::TypeError`.
### Steps to reproduce
```rb
~/src/github.com/protocolbuffers/protobuf/ruby/tests ❯❯❯ irb -I .
irb(main):001:0> require 'basic_test_pb'
=> true
irb(main):002:0> myString = Class.new(String)
=> #<Class:0x00000001531540d8>
irb(main):003:0> str = myString.new("foo")
=> "foo"
irb(main):004:0> BasicTest::TestMessage.new(optional_string: "foo")
=> <BasicTest::TestMessage: optional_string: "foo", repeated_int32: [], repeated_int64: [], repeated_uint32: [], repeated_uint64: [], repeated_bool: [], repeated_float: [], repeated_double: [], repeated_string: [], repeated_bytes: [], repeated_msg: [], repeated_enum: []>
irb(main):005:0> BasicTest::TestMessage.new(optional_string: str)
(irb):5:in `initialize': Invalid argument for string field 'optional_string' (given #<Class:0x00000001531540d8>). (Google::Protobuf::TypeError)
irb(main):006:0>
```
## Fix
The issue appears to be caused by the field checking mechanism not properly handling instances of classes that inherit from basic types like String. My proposed solution is to improve the type checking for string fields to consider not just String instances but also instances of subclasses of String.
## Impact
The changes will allow instances of classes derived from String to be passed to string fields without any error.
This is a backwards-compatible change and will not affect the existing behaviour with standard String instances.
Closes #13818
COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/13818 from NotFounds:support-string-subclass-for-ruby 2d2796c4f96d5a182a269b54e893f436f2211ea2
PiperOrigin-RevId: 590941235
12 months ago
|
|
|
|
|
|
|
def test_string_subclass
|
|
|
|
str = "hello"
|
|
|
|
myString = Class.new(String)
|
|
|
|
|
|
|
|
m = proto_module::TestMessage.new(
|
|
|
|
optional_string: myString.new(str),
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_equal str, m.optional_string
|
|
|
|
end
|
|
|
|
end
|