1#!/usr/bin/ruby 2 3# generated_code.rb is in the same directory as this test. 4$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) 5 6require 'basic_test_proto2_pb' 7require 'generated_code_pb' 8require 'google/protobuf/well_known_types' 9require 'test/unit' 10 11module CaptureWarnings 12 @@warnings = nil 13 14 module_function 15 16 def warn(message, category: nil, **kwargs) 17 if @@warnings 18 @@warnings << message 19 else 20 super 21 end 22 end 23 24 def capture 25 @@warnings = [] 26 yield 27 @@warnings 28 ensure 29 @@warnings = nil 30 end 31end 32 33Warning.extend CaptureWarnings 34 35def hex2bin(s) 36 s.scan(/../).map { |x| x.hex.chr }.join 37end 38 39class NonConformantNumericsTest < Test::Unit::TestCase 40 def test_empty_json_numerics 41 if defined? JRUBY_VERSION and Google::Protobuf::IMPLEMENTATION != :FFI 42 # In a future version, CRuby and JRuby FFI will also have this behavior. 43 assert_raises Google::Protobuf::ParseError do 44 msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalInt32":""}') 45 end 46 else 47 warnings = CaptureWarnings.capture { 48 msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalInt32":""}') 49 assert_equal 0, msg.optional_int32 50 assert msg.has_optional_int32? 51 } 52 assert_equal 1, warnings.size 53 assert_match "Empty string is not a valid number (field: basic_test_proto2.TestMessage.optional_int32)", warnings[0] 54 end 55 end 56 57 def test_trailing_non_numeric_characters 58 if defined? JRUBY_VERSION and Google::Protobuf::IMPLEMENTATION != :FFI 59 # In a future version, CRuby and JRuby FFI will also have this behavior. 60 assert_raises Google::Protobuf::ParseError do 61 msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalDouble":"123abc"}') 62 end 63 else 64 warnings = CaptureWarnings.capture { 65 msg = ::BasicTestProto2::TestMessage.decode_json('{"optionalDouble":"123abc"}') 66 assert_equal 123, msg.optional_double 67 assert msg.has_optional_double? 68 } 69 assert_equal 1, warnings.size 70 assert_match "Non-number characters in quoted number (field: basic_test_proto2.TestMessage.optional_double)", warnings[0] 71 end 72 end 73end 74 75class EncodeDecodeTest < Test::Unit::TestCase 76 def test_discard_unknown 77 # Test discard unknown in message. 78 unknown_msg = A::B::C::TestUnknown.new(:unknown_field => 1) 79 from = A::B::C::TestUnknown.encode(unknown_msg) 80 m = A::B::C::TestMessage.decode(from) 81 Google::Protobuf.discard_unknown(m) 82 to = A::B::C::TestMessage.encode(m) 83 assert_empty to 84 # Test discard unknown for singular message field. 85 unknown_msg = A::B::C::TestUnknown.new( 86 :optional_unknown => 87 A::B::C::TestUnknown.new(:unknown_field => 1)) 88 from = A::B::C::TestUnknown.encode(unknown_msg) 89 m = A::B::C::TestMessage.decode(from) 90 Google::Protobuf.discard_unknown(m) 91 to = A::B::C::TestMessage.encode(m.optional_msg) 92 assert_empty to 93 # Test discard unknown for repeated message field. 94 unknown_msg = A::B::C::TestUnknown.new( 95 :repeated_unknown => 96 [A::B::C::TestUnknown.new(:unknown_field => 1)]) 97 from = A::B::C::TestUnknown.encode(unknown_msg) 98 m = A::B::C::TestMessage.decode(from) 99 Google::Protobuf.discard_unknown(m) 100 to = A::B::C::TestMessage.encode(m.repeated_msg[0]) 101 assert_empty to 102 # Test discard unknown for map value message field. 103 unknown_msg = A::B::C::TestUnknown.new( 104 :map_unknown => 105 {"" => A::B::C::TestUnknown.new(:unknown_field => 1)}) 106 from = A::B::C::TestUnknown.encode(unknown_msg) 107 m = A::B::C::TestMessage.decode(from) 108 Google::Protobuf.discard_unknown(m) 109 to = A::B::C::TestMessage.encode(m.map_string_msg['']) 110 assert_empty to 111 # Test discard unknown for oneof message field. 112 unknown_msg = A::B::C::TestUnknown.new( 113 :oneof_unknown => 114 A::B::C::TestUnknown.new(:unknown_field => 1)) 115 from = A::B::C::TestUnknown.encode(unknown_msg) 116 m = A::B::C::TestMessage.decode(from) 117 Google::Protobuf.discard_unknown(m) 118 to = A::B::C::TestMessage.encode(m.oneof_msg) 119 assert_empty to 120 end 121 122 def test_encode_json 123 msg = A::B::C::TestMessage.new({ optional_int32: 22 }) 124 json = msg.to_json 125 126 to = A::B::C::TestMessage.decode_json(json) 127 assert_equal 22, to.optional_int32 128 msg = A::B::C::TestMessage.new({ optional_int32: 22 }) 129 json = msg.to_json({ preserve_proto_fieldnames: true }) 130 131 assert_match 'optional_int32', json 132 133 to = A::B::C::TestMessage.decode_json(json) 134 assert_equal 22, to.optional_int32 135 136 msg = A::B::C::TestMessage.new({ optional_int32: 22 }) 137 json = A::B::C::TestMessage.encode_json( 138 msg, 139 { preserve_proto_fieldnames: true, emit_defaults: true } 140 ) 141 142 assert_match 'optional_int32', json 143 144 145 # Test for enums printing as ints. 146 msg = A::B::C::TestMessage.new({ optional_enum: 1 }) 147 json = A::B::C::TestMessage.encode_json( 148 msg, 149 { :format_enums_as_integers => true } 150 ) 151 152 assert_match '"optionalEnum":1', json 153 154 # Test for default enum being printed as int. 155 msg = A::B::C::TestMessage.new({ optional_enum: 0 }) 156 json = A::B::C::TestMessage.encode_json( 157 msg, 158 { :format_enums_as_integers => true, :emit_defaults => true } 159 ) 160 161 assert_match '"optionalEnum":0', json 162 163 # Test for repeated enums printing as ints. 164 msg = A::B::C::TestMessage.new({ repeated_enum: [0,1,2,3] }) 165 json = A::B::C::TestMessage.encode_json( 166 msg, 167 { :format_enums_as_integers => true } 168 ) 169 170 assert_match '"repeatedEnum":[0,1,2,3]', json 171 end 172 173 def test_encode_wrong_msg 174 assert_raises ::ArgumentError do 175 m = A::B::C::TestMessage.new( 176 :optional_int32 => 1, 177 ) 178 Google::Protobuf::Any.encode(m) 179 end 180 end 181 182 def test_json_name 183 msg = A::B::C::TestJsonName.new(:value => 42) 184 json = msg.to_json 185 assert_match json, "{\"CustomJsonName\":42}" 186 end 187 188 def test_decode_depth_limit 189 msg = A::B::C::TestMessage.new( 190 optional_msg: A::B::C::TestMessage.new( 191 optional_msg: A::B::C::TestMessage.new( 192 optional_msg: A::B::C::TestMessage.new( 193 optional_msg: A::B::C::TestMessage.new( 194 optional_msg: A::B::C::TestMessage.new( 195 ) 196 ) 197 ) 198 ) 199 ) 200 ) 201 msg_encoded = A::B::C::TestMessage.encode(msg) 202 msg_out = A::B::C::TestMessage.decode(msg_encoded) 203 assert_match msg.to_json, msg_out.to_json 204 205 assert_raises Google::Protobuf::ParseError do 206 A::B::C::TestMessage.decode(msg_encoded, { recursion_limit: 4 }) 207 end 208 209 msg_out = A::B::C::TestMessage.decode(msg_encoded, { recursion_limit: 5 }) 210 assert_match msg.to_json, msg_out.to_json 211 end 212 213 def test_encode_depth_limit 214 msg = A::B::C::TestMessage.new( 215 optional_msg: A::B::C::TestMessage.new( 216 optional_msg: A::B::C::TestMessage.new( 217 optional_msg: A::B::C::TestMessage.new( 218 optional_msg: A::B::C::TestMessage.new( 219 optional_msg: A::B::C::TestMessage.new( 220 ) 221 ) 222 ) 223 ) 224 ) 225 ) 226 msg_encoded = A::B::C::TestMessage.encode(msg) 227 msg_out = A::B::C::TestMessage.decode(msg_encoded) 228 assert_match msg.to_json, msg_out.to_json 229 230 assert_raises RuntimeError do 231 A::B::C::TestMessage.encode(msg, { recursion_limit: 5 }) 232 end 233 234 msg_encoded = A::B::C::TestMessage.encode(msg, { recursion_limit: 6 }) 235 msg_out = A::B::C::TestMessage.decode(msg_encoded) 236 assert_match msg.to_json, msg_out.to_json 237 end 238 239end 240