diff options
Diffstat (limited to 'test/json/json_parser_test.rb')
| -rw-r--r-- | test/json/json_parser_test.rb | 565 |
1 files changed, 461 insertions, 104 deletions
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 146ff7c047..ac53ba9f0c 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -1,10 +1,15 @@ -# encoding: utf-8 -# frozen_string_literal: false -require 'test_helper' +# frozen_string_literal: true +require_relative 'test_helper' require 'stringio' require 'tempfile' -require 'ostruct' -require 'bigdecimal' +begin + require 'ostruct' +rescue LoadError +end +begin + require 'bigdecimal' +rescue LoadError +end class JSONParserTest < Test::Unit::TestCase include JSON @@ -14,21 +19,28 @@ class JSONParserTest < Test::Unit::TestCase assert_equal 'test', parser.source end - def test_argument_encoding - source = "{}".encode("UTF-16") + def test_argument_encoding_unmodified + source = "{}".encode(Encoding::UTF_16) JSON::Parser.new(source) assert_equal Encoding::UTF_16, source.encoding - end if defined?(Encoding::UTF_16) + end + + def test_argument_encoding_for_binary_unmodified + source = "{}".b + JSON::Parser.new(source) + assert_equal Encoding::ASCII_8BIT, source.encoding + end def test_error_message_encoding bug10705 = '[ruby-core:67386] [Bug #10705]' - json = ".\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8) + json = ".\"\xE2\x88\x9A\"" + assert_equal(Encoding::UTF_8, json.encoding) e = assert_raise(JSON::ParserError) { JSON::Ext::Parser.new(json).parse } assert_equal(Encoding::UTF_8, e.message.encoding, bug10705) assert_include(e.message, json, bug10705) - end if defined?(Encoding::UTF_8) and defined?(JSON::Ext::Parser) + end def test_parsing parser = JSON::Parser.new('"test"') @@ -92,6 +104,14 @@ class JSONParserTest < Test::Unit::TestCase assert_raise(JSON::ParserError) { parse('+23') } assert_raise(JSON::ParserError) { parse('.23') } assert_raise(JSON::ParserError) { parse('023') } + assert_raise(JSON::ParserError) { parse('-023') } + assert_raise(JSON::ParserError) { parse('023.12') } + assert_raise(JSON::ParserError) { parse('-023.12') } + assert_raise(JSON::ParserError) { parse('023e12') } + assert_raise(JSON::ParserError) { parse('-023e12') } + assert_raise(JSON::ParserError) { parse('-') } + assert_raise(JSON::ParserError) { parse('-.1') } + assert_raise(JSON::ParserError) { parse('-e0') } assert_equal(23, parse('23')) assert_equal(-23, parse('-23')) assert_equal_float(3.141, parse('3.141')) @@ -108,37 +128,56 @@ class JSONParserTest < Test::Unit::TestCase assert_equal(1.0/0, parse('Infinity', :allow_nan => true)) assert_raise(ParserError) { parse('-Infinity') } assert_equal(-1.0/0, parse('-Infinity', :allow_nan => true)) + capture_output { assert_equal(Float::INFINITY, parse("23456789012E666")) } + end + + def test_parse_bignum + bignum = Integer('1234567890' * 10) + assert_equal(bignum, JSON.parse(bignum.to_s)) + assert_equal(bignum.to_f, JSON.parse(bignum.to_s + ".0")) end def test_parse_bigdecimals assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class) assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] ) - end + end if defined?(::BigDecimal) def test_parse_string_mixed_unicode assert_equal(["éé"], JSON.parse("[\"\\u00e9é\"]")) end - if Array.method_defined?(:permutation) - def test_parse_more_complex_arrays - a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] - a.permutation.each do |perm| - json = pretty_generate(perm) - assert_equal perm, parse(json) - end + def test_parse_more_complex_arrays + a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] + a.permutation.each do |perm| + json = pretty_generate(perm) + assert_equal perm, parse(json) end + end + + def test_parse_complex_objects + a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] + a.permutation.each do |perm| + s = "a" + orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h } + json = pretty_generate(orig_obj) + assert_equal orig_obj, parse(json) + end + end - def test_parse_complex_objects - a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }] - a.permutation.each do |perm| - s = "a" - orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h } - json = pretty_generate(orig_obj) - assert_equal orig_obj, parse(json) + def test_parse_control_chars_in_string + 0.upto(31) do |ord| + assert_raise JSON::ParserError do + parse(%("#{ord.chr}")) end end end + def test_parse_allowed_control_chars_in_string + 0.upto(31) do |ord| + assert_equal ord.chr, parse(%("#{ord.chr}"), allow_control_characters: true) + end + end + def test_parse_arrays assert_equal([1,2,3], parse('[1,2,3]')) assert_equal([1.2,2,3], parse('[1.2,2,3]')) @@ -170,7 +209,93 @@ class JSONParserTest < Test::Unit::TestCase assert parse('NaN', :allow_nan => true).nan? assert parse('Infinity', :allow_nan => true).infinite? assert parse('-Infinity', :allow_nan => true).infinite? - assert_raise(JSON::ParserError) { parse('[ 1, ]') } + end + + def test_parse_arrays_with_allow_trailing_comma + assert_equal([], parse('[]', allow_trailing_comma: true)) + assert_equal([], parse('[]', allow_trailing_comma: false)) + assert_raise(JSON::ParserError) { parse('[,]', allow_trailing_comma: true) } + assert_raise(JSON::ParserError) { parse('[,]', allow_trailing_comma: false) } + + assert_equal([1], parse('[1]', allow_trailing_comma: true)) + assert_equal([1], parse('[1]', allow_trailing_comma: false)) + assert_equal([1], parse('[1,]', allow_trailing_comma: true)) + assert_raise(JSON::ParserError) { parse('[1,]', allow_trailing_comma: false) } + + assert_equal([1, 2, 3], parse('[1,2,3]', allow_trailing_comma: true)) + assert_equal([1, 2, 3], parse('[1,2,3]', allow_trailing_comma: false)) + assert_equal([1, 2, 3], parse('[1,2,3,]', allow_trailing_comma: true)) + assert_raise(JSON::ParserError) { parse('[1,2,3,]', allow_trailing_comma: false) } + + assert_equal([1, 2, 3], parse('[ 1 , 2 , 3 ]', allow_trailing_comma: true)) + assert_equal([1, 2, 3], parse('[ 1 , 2 , 3 ]', allow_trailing_comma: false)) + assert_equal([1, 2, 3], parse('[ 1 , 2 , 3 , ]', allow_trailing_comma: true)) + assert_raise(JSON::ParserError) { parse('[ 1 , 2 , 3 , ]', allow_trailing_comma: false) } + + assert_equal({'foo' => [1, 2, 3]}, parse('{ "foo": [1,2,3] }', allow_trailing_comma: true)) + assert_equal({'foo' => [1, 2, 3]}, parse('{ "foo": [1,2,3] }', allow_trailing_comma: false)) + assert_equal({'foo' => [1, 2, 3]}, parse('{ "foo": [1,2,3,] }', allow_trailing_comma: true)) + assert_raise(JSON::ParserError) { parse('{ "foo": [1,2,3,] }', allow_trailing_comma: false) } + end + + def test_parse_object_with_allow_trailing_comma + assert_equal({}, parse('{}', allow_trailing_comma: true)) + assert_equal({}, parse('{}', allow_trailing_comma: false)) + assert_raise(JSON::ParserError) { parse('{,}', allow_trailing_comma: true) } + assert_raise(JSON::ParserError) { parse('{,}', allow_trailing_comma: false) } + + assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}', allow_trailing_comma: true)) + assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}', allow_trailing_comma: false)) + assert_equal({'foo'=>'bar'}, parse('{"foo":"bar",}', allow_trailing_comma: true)) + assert_raise(JSON::ParserError) { parse('{"foo":"bar",}', allow_trailing_comma: false) } + + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{"foo":"bar","baz":"qux","quux":"garply"}', allow_trailing_comma: true) + ) + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{"foo":"bar","baz":"qux","quux":"garply"}', allow_trailing_comma: false) + ) + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{"foo":"bar","baz":"qux","quux":"garply",}', allow_trailing_comma: true) + ) + assert_raise(JSON::ParserError) { + parse('{"foo":"bar","baz":"qux","quux":"garply",}', allow_trailing_comma: false) + } + + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{ "foo":"bar" , "baz":"qux" , "quux":"garply" }', allow_trailing_comma: true) + ) + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{ "foo":"bar" , "baz":"qux" , "quux":"garply" }', allow_trailing_comma: false) + ) + assert_equal( + {'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}, + parse('{ "foo":"bar" , "baz":"qux" , "quux":"garply" , }', allow_trailing_comma: true) + ) + assert_raise(JSON::ParserError) { + parse('{ "foo":"bar" , "baz":"qux" , "quux":"garply" , }', allow_trailing_comma: false) + } + + assert_equal( + [{'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}], + parse('[{"foo":"bar","baz":"qux","quux":"garply"}]', allow_trailing_comma: true) + ) + assert_equal( + [{'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}], + parse('[{"foo":"bar","baz":"qux","quux":"garply"}]', allow_trailing_comma: false) + ) + assert_equal( + [{'foo'=>'bar', 'baz'=>'qux', 'quux'=>'garply'}], + parse('[{"foo":"bar","baz":"qux","quux":"garply",}]', allow_trailing_comma: true) + ) + assert_raise(JSON::ParserError) { + parse('[{"foo":"bar","baz":"qux","quux":"garply",}]', allow_trailing_comma: false) + } end def test_parse_some_strings @@ -184,6 +309,50 @@ class JSONParserTest < Test::Unit::TestCase ) end + if RUBY_ENGINE != "jruby" # https://github.com/ruby/json/issues/138 + def test_parse_broken_string + s = parse(%{["\x80"]})[0] + assert_equal("\x80", s) + assert_equal Encoding::UTF_8, s.encoding + assert_equal false, s.valid_encoding? + + s = parse(%{["\x80"]}.b)[0] + assert_equal("\x80", s) + assert_equal Encoding::UTF_8, s.encoding + assert_equal false, s.valid_encoding? + + input = %{["\x80"]}.dup.force_encoding(Encoding::US_ASCII) + assert_raise(Encoding::InvalidByteSequenceError) { parse(input) } + end + end + + def test_invalid_unicode_escape + assert_raise(JSON::ParserError) { parse('"\u"') } + assert_raise(JSON::ParserError) { parse('"\ua"') } + assert_raise(JSON::ParserError) { parse('"\uaa"') } + assert_raise(JSON::ParserError) { parse('"\uaaa"') } + assert_equal "\uaaaa", parse('"\uaaaa"') + + assert_raise(JSON::ParserError) { parse('"\u______"') } + assert_raise(JSON::ParserError) { parse('"\u1_____"') } + assert_raise(JSON::ParserError) { parse('"\u11____"') } + assert_raise(JSON::ParserError) { parse('"\u111___"') } + end + + def test_unicode_followed_by_newline + # Ref: https://github.com/ruby/json/issues/912 + assert_equal "🌌\n".bytes, JSON.parse('"\ud83c\udf0c\n"').bytes + assert_equal "🌌\n", JSON.parse('"\ud83c\udf0c\n"') + assert_predicate JSON.parse('"\ud83c\udf0c\n"'), :valid_encoding? + end + + def test_invalid_surogates + assert_raise(JSON::ParserError) { parse('"\\uD800"') } + assert_raise(JSON::ParserError) { parse('"\\uD800_________________"') } + assert_raise(JSON::ParserError) { parse('"\\uD800\\u0041"') } + assert_raise(JSON::ParserError) { parse('"\\uD800\\u004') } + end + def test_parse_big_integers json1 = JSON(orig = (1 << 31) - 1) assert_equal orig, parse(json1) @@ -197,6 +366,52 @@ class JSONParserTest < Test::Unit::TestCase assert_equal orig, parse(json5) end + def test_parse_escaped_key + doc = { + "test\r1" => 1, + "entries" => [ + "test\t2" => 2, + "test\n3" => 3, + ] + } + + assert_equal doc, parse(JSON.generate(doc)) + end + + def test_parse_duplicate_key + expected = {"a" => 2} + expected_sym = {a: 2} + + assert_equal expected, parse('{"a": 1, "a": 2}', allow_duplicate_key: true) + assert_raise(ParserError) { parse('{"a": 1, "a": 2}', allow_duplicate_key: false) } + assert_raise(ParserError) { parse('{"a": 1, "a": 2}', allow_duplicate_key: false, symbolize_names: true) } + + assert_deprecated_warning(/duplicate key "a"/) do + assert_equal expected, parse('{"a": 1, "a": 2}') + end + assert_deprecated_warning(/duplicate key "a"/) do + assert_equal expected_sym, parse('{"a": 1, "a": 2}', symbolize_names: true) + end + + if RUBY_ENGINE == 'ruby' + assert_deprecated_warning(/#{File.basename(__FILE__)}\:#{__LINE__ + 1}/) do + assert_equal expected, parse('{"a": 1, "a": 2}') + end + end + + unless RUBY_ENGINE == 'jruby' + assert_raise(ParserError) do + fake_key = Object.new + JSON.load('{"a": 1, "a": 2}', -> (obj) { obj == "a" ? fake_key : obj }, allow_duplicate_key: false) + end + + assert_deprecated_warning(/duplicate key #<Object:0x/) do + fake_key = Object.new + JSON.load('{"a": 1, "a": 2}', -> (obj) { obj == "a" ? fake_key : obj }) + end + end + end + def test_some_wrong_inputs assert_raise(ParserError) { parse('[] bla') } assert_raise(ParserError) { parse('[] 1') } @@ -228,50 +443,61 @@ class JSONParserTest < Test::Unit::TestCase assert_predicate parse('[]', :freeze => true), :frozen? assert_predicate parse('"foo"', :freeze => true), :frozen? - if string_deduplication_available? - assert_same(-'foo', parse('"foo"', :freeze => true)) - assert_same(-'foo', parse('{"foo": 1}', :freeze => true).keys.first) - end + assert_same(-'foo', parse('"foo"', :freeze => true)) + assert_same(-'foo', parse('{"foo": 1}', :freeze => true).keys.first) end def test_parse_comments - json = <<EOT -{ - "key1":"value1", // eol comment - "key2":"value2" /* multi line - * comment */, - "key3":"value3" /* multi line - // nested eol comment - * comment */ -} -EOT + json = <<~JSON + { + "key1":"value1", // eol comment + "key2":"value2" /* multi line + * comment */, + "key3":"value3" /* multi line + // nested eol comment + * comment */ + } + JSON assert_equal( { "key1" => "value1", "key2" => "value2", "key3" => "value3" }, parse(json)) - json = <<EOT -{ - "key1":"value1" /* multi line - // nested eol comment - /* illegal nested multi line comment */ - * comment */ -} -EOT + json = <<~JSON + { + "key1":"value1" /* multi line + // nested eol comment + /* illegal nested multi line comment */ + * comment */ + } + JSON assert_raise(ParserError) { parse(json) } - json = <<EOT -{ - "key1":"value1" /* multi line - // nested eol comment - closed multi comment */ - and again, throw an Error */ -} -EOT + json = <<~JSON + { + "key1":"value1" /* multi line + // nested eol comment + /* legal nested multi line comment start sequence */ + } + JSON + assert_equal({ "key1" => "value1" }, parse(json)) + json = <<~JSON + { + "key1":"value1" /* multi line + // nested eol comment + closed multi comment */ + and again, throw an Error */ + } + JSON assert_raise(ParserError) { parse(json) } - json = <<EOT -{ - "key1":"value1" /*/*/ -} -EOT + json = <<~JSON + { + "key1":"value1" /*/*/ + } + JSON assert_equal({ "key1" => "value1" }, parse(json)) + assert_equal({}, parse('{} /**/')) + assert_raise(ParserError) { parse('{} /* comment not closed') } + assert_raise(ParserError) { parse('{} /*/') } + assert_raise(ParserError) { parse('{} /x wrong comment') } + assert_raise(ParserError) { parse('{} /') } end def test_nesting @@ -306,13 +532,97 @@ EOT data = ['"'] assert_equal data, parse(json) # - json = '["\\\'"]' - data = ["'"] + json = '["\\/"]' + data = ["/"] assert_equal data, parse(json) json = '["\/"]' data = [ '/' ] assert_equal data, parse(json) + + data = ['"""""""""""""""""""""""""'] + json = '["\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\""]' + assert_equal data, parse(json) + + data = '["This is a "test" of the emergency broadcast system."]' + json = "\"[\\\"This is a \\\"test\\\" of the emergency broadcast system.\\\"]\"" + assert_equal data, parse(json) + + data = '\tThis is a test of the emergency broadcast system.' + json = "\"\\\\tThis is a test of the emergency broadcast system.\"" + assert_equal data, parse(json) + + data = 'This\tis a test of the emergency broadcast system.' + json = "\"This\\\\tis a test of the emergency broadcast system.\"" + assert_equal data, parse(json) + + data = 'This is\ta test of the emergency broadcast system.' + json = "\"This is\\\\ta test of the emergency broadcast system.\"" + assert_equal data, parse(json) + + data = 'This is a test of the emergency broadcast\tsystem.' + json = "\"This is a test of the emergency broadcast\\\\tsystem.\"" + assert_equal data, parse(json) + + data = 'This is a test of the emergency broadcast\tsystem.\n' + json = "\"This is a test of the emergency broadcast\\\\tsystem.\\\\n\"" + assert_equal data, parse(json) + + data = '"' * 15 + json = "\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\"" + assert_equal data, parse(json) + + data = "\"\"\"\"\"\"\"\"\"\"\"\"\"\"a" + json = "\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"\\\"a\"" + assert_equal data, parse(json) + + data = "\u0001\u0001\u0001\u0001" + json = "\"\\u0001\\u0001\\u0001\\u0001\"" + assert_equal data, parse(json) + + data = "\u0001a\u0001a\u0001a\u0001a" + json = "\"\\u0001a\\u0001a\\u0001a\\u0001a\"" + assert_equal data, parse(json) + + data = "\u0001aa\u0001aa" + json = "\"\\u0001aa\\u0001aa\"" + assert_equal data, parse(json) + + data = "\u0001aa\u0001aa\u0001aa" + json = "\"\\u0001aa\\u0001aa\\u0001aa\"" + assert_equal data, parse(json) + + data = "\u0001aa\u0001aa\u0001aa\u0001aa\u0001aa\u0001aa" + json = "\"\\u0001aa\\u0001aa\\u0001aa\\u0001aa\\u0001aa\\u0001aa\"" + assert_equal data, parse(json) + + data = "\u0001a\u0002\u0001a\u0002\u0001a\u0002\u0001a\u0002\u0001a\u0002\u0001a\u0002\u0001a\u0002\u0001a\u0002" + json = "\"\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\\u0001a\\u0002\"" + assert_equal data, parse(json) + + data = "ab\u0002c" + json = "\"ab\\u0002c\"" + assert_equal data, parse(json) + + data = "ab\u0002cab\u0002cab\u0002cab\u0002c" + json = "\"ab\\u0002cab\\u0002cab\\u0002cab\\u0002c\"" + assert_equal data, parse(json) + + data = "ab\u0002cab\u0002cab\u0002cab\u0002cab\u0002cab\u0002c" + json = "\"ab\\u0002cab\\u0002cab\\u0002cab\\u0002cab\\u0002cab\\u0002c\"" + assert_equal data, parse(json) + + data = "\n\t\f\b\n\t\f\b\n\t\f\b\n\t\f" + json = "\"\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\\f\"" + assert_equal data, parse(json) + + data = "\n\t\f\b\n\t\f\b\n\t\f\b\n\t\f\b" + json = "\"\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\\f\\b\"" + assert_equal data, parse(json) + + data = "a\n\t\f\b\n\t\f\b\n\t\f\b\n\t" + json = "\"a\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\\f\\b\\n\\t\"" + assert_equal data, parse(json) end class SubArray < Array @@ -371,6 +681,7 @@ EOT def test_parse_array_custom_non_array_derived_class res = parse('[1,2]', :array_class => SubArrayWrapper) assert_equal([1,2], res.data) + assert_equal(1, res[0]) assert_equal(SubArrayWrapper, res.class) assert res.shifted? end @@ -406,21 +717,6 @@ EOT end end - class SubOpenStruct < OpenStruct - def [](k) - __send__(k) - end - - def []=(k, v) - @item_set = true - __send__("#{k}=", v) - end - - def item_set? - @item_set - end - end - def test_parse_object_custom_hash_derived_class res = parse('{"foo":"bar"}', :object_class => SubHash) assert_equal({"foo" => "bar"}, res) @@ -428,24 +724,42 @@ EOT assert res.item_set? end - def test_parse_object_custom_non_hash_derived_class - res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) - assert_equal "bar", res.foo - assert_equal(SubOpenStruct, res.class) - assert res.item_set? - end + if defined?(::OpenStruct) + class SubOpenStruct < OpenStruct + def [](k) + __send__(k) + end - def test_parse_generic_object - res = parse( - '{"foo":"bar", "baz":{}}', - :object_class => JSON::GenericObject - ) - assert_equal(JSON::GenericObject, res.class) - assert_equal "bar", res.foo - assert_equal "bar", res["foo"] - assert_equal "bar", res[:foo] - assert_equal "bar", res.to_hash[:foo] - assert_equal(JSON::GenericObject, res.baz.class) + def []=(k, v) + @item_set = true + __send__("#{k}=", v) + end + + def item_set? + @item_set + end + end + + def test_parse_object_custom_non_hash_derived_class + res = parse('{"foo":"bar"}', :object_class => SubOpenStruct) + assert_equal "bar", res.foo + assert_equal "bar", res[:foo] + assert_equal(SubOpenStruct, res.class) + assert res.item_set? + end + + def test_parse_generic_object + res = parse( + '{"foo":"bar", "baz":{}}', + :object_class => JSON::GenericObject + ) + assert_equal(JSON::GenericObject, res.class) + assert_equal "bar", res.foo + assert_equal "bar", res["foo"] + assert_equal "bar", res[:foo] + assert_equal "bar", res.to_hash[:foo] + assert_equal(JSON::GenericObject, res.baz.class) + end end def test_generate_core_subclasses_with_new_to_json @@ -478,22 +792,65 @@ EOT def test_parsing_frozen_ascii8bit_string assert_equal( { 'foo' => 'bar' }, - JSON('{ "foo": "bar" }'.force_encoding(Encoding::ASCII_8BIT).freeze) + JSON('{ "foo": "bar" }'.b.freeze) ) end - private + def test_parse_error_message_length + # Error messages aren't consistent across backends, but we can at least + # enforce that if they include fragments of the source it should be of + # reasonable size. + error = assert_raise(JSON::ParserError) do + JSON.parse('{"foo": ' + ('A' * 500) + '}') + end + assert_operator 80, :>, error.message.bytesize + end - def string_deduplication_available? - r1 = rand.to_s - r2 = r1.dup - begin - (-r1).equal?(-r2) - rescue NoMethodError - false # No String#-@ + def test_parse_error_incomplete_hash + error = assert_raise(JSON::ParserError) do + JSON.parse('{"input":{"firstName":"Bob","lastName":"Mob","email":"bob@example.com"}') + end + if RUBY_ENGINE == "ruby" + assert_equal %(expected ',' or '}' after object value, got: EOF at line 1 column 72), error.message end end + def test_parse_error_snippet + omit "C ext only test" unless RUBY_ENGINE == "ruby" + + error = assert_raise(JSON::ParserError) { JSON.parse("あああああああああああああああああああああああ") } + assert_equal "unexpected character: 'ああああああああああ' at line 1 column 1", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("aあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'aああああああああああ' at line 1 column 1", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("abあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'abあああああああああ' at line 1 column 1", error.message + + error = assert_raise(JSON::ParserError) { JSON.parse("abcあああああああああああああああああああああああ") } + assert_equal "unexpected character: 'abcあああああああああ' at line 1 column 1", error.message + end + + def test_parse_leading_slash + # ref: https://github.com/ruby/ruby/pull/12598 + assert_raise(JSON::ParserError) do + JSON.parse("/foo/bar") + end + end + + def test_parse_whitespace_after_newline + assert_equal [], JSON.parse("[\n#{' ' * (8 + 8 + 4 + 3)}]") + end + + def test_frozen + parser_config = JSON::Parser::Config.new({}).freeze + assert_raise FrozenError do + parser_config.send(:initialize, {}) + end + end + + private + def assert_equal_float(expected, actual, delta = 1e-2) Array === expected and expected = expected.first Array === actual and actual = actual.first |
