summaryrefslogtreecommitdiff
path: root/test/json/json_parser_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/json/json_parser_test.rb')
-rw-r--r--test/json/json_parser_test.rb263
1 files changed, 239 insertions, 24 deletions
diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb
index 87b78fb0ca..292ca1a670 100644
--- a/test/json/json_parser_test.rb
+++ b/test/json/json_parser_test.rb
@@ -128,6 +128,18 @@ 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"))
+
+ bignum = Integer('1234567890' * 50)
+ assert_equal(bignum, JSON.parse(bignum.to_s))
+ bignum_float = EnvUtil.suppress_warning { bignum.to_f }
+ assert_equal(bignum_float, EnvUtil.suppress_warning { JSON.parse(bignum.to_s + ".0") })
end
def test_parse_bigdecimals
@@ -157,6 +169,37 @@ class JSONParserTest < Test::Unit::TestCase
end
end
+ 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_control_char_and_backslash
+ backslash_and_control_char = "\\\t"
+ assert_raise JSON::ParserError do
+ JSON.parse(%("#{'a' * 30}#{backslash_and_control_char}"), allow_control_characters: true, allow_invalid_escape: false)
+ end
+
+ JSON.parse(%("#{'a' * 30}#{backslash_and_control_char}"), allow_control_characters: true, allow_invalid_escape: true)
+ end
+
+ def test_parse_invalid_escape
+ assert_raise JSON::ParserError do
+ parse(%("fo\\o"))
+ end
+
+ assert_equal "foo", parse(%("fo\\o"), allow_invalid_escape: true)
+ 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]'))
@@ -318,6 +361,20 @@ class JSONParserTest < Test::Unit::TestCase
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)
@@ -331,6 +388,59 @@ 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_parse_duplicate_key_escape
+ error = assert_raise(ParserError) do
+ JSON.parse('{"%s%s%s%s":1,"%s%s%s%s":2}', allow_duplicate_key: false)
+ end
+ assert_match "%s%s%s%s", error.message
+ end
+
def test_some_wrong_inputs
assert_raise(ParserError) { parse('[] bla') }
assert_raise(ParserError) { parse('[] 1') }
@@ -362,10 +472,8 @@ 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
@@ -437,29 +545,117 @@ class JSONParserTest < Test::Unit::TestCase
end
def test_backslash
+ assert_raise(JSON::ParserError) do
+ JSON.parse('"\\')
+ end
+
data = [ '\\.(?i:gif|jpe?g|png)$' ]
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
assert_equal data, parse(json)
- #
+
data = [ '\\"' ]
json = '["\\\\\""]'
assert_equal data, parse(json)
- #
+
json = '["/"]'
data = [ '/' ]
assert_equal data, parse(json)
- #
+
json = '["\""]'
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
@@ -518,6 +714,7 @@ class JSONParserTest < Test::Unit::TestCase
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
@@ -579,6 +776,7 @@ class JSONParserTest < Test::Unit::TestCase
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
@@ -638,7 +836,7 @@ class JSONParserTest < Test::Unit::TestCase
error = assert_raise(JSON::ParserError) do
JSON.parse('{"foo": ' + ('A' * 500) + '}')
end
- assert_operator 60, :>, error.message.bytesize
+ assert_operator 80, :>, error.message.bytesize
end
def test_parse_error_incomplete_hash
@@ -646,7 +844,7 @@ class JSONParserTest < Test::Unit::TestCase
JSON.parse('{"input":{"firstName":"Bob","lastName":"Mob","email":"bob@example.com"}')
end
if RUBY_ENGINE == "ruby"
- assert_equal %(expected ',' or '}' after object value, got: ''), error.message
+ assert_equal %(expected ',' or '}' after object value, got: EOF at line 1 column 72), error.message
end
end
@@ -654,16 +852,16 @@ class JSONParserTest < Test::Unit::TestCase
omit "C ext only test" unless RUBY_ENGINE == "ruby"
error = assert_raise(JSON::ParserError) { JSON.parse("あああああああああああああああああああああああ") }
- assert_equal "unexpected character: 'ああああああああああ'", error.message
+ assert_equal "unexpected character: 'ああああああああああ' at line 1 column 1", error.message
error = assert_raise(JSON::ParserError) { JSON.parse("aあああああああああああああああああああああああ") }
- assert_equal "unexpected character: 'aああああああああああ'", error.message
+ 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あああああああああ'", error.message
+ 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あああああああああ'", error.message
+ assert_equal "unexpected character: 'abcあああああああああ' at line 1 column 1", error.message
end
def test_parse_leading_slash
@@ -673,18 +871,35 @@ class JSONParserTest < Test::Unit::TestCase
end
end
- private
+ 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
+
+ def test_mutating_source_string_during_parsing
+ expected = ([1] * 100) + [2.3] + ([1] * 100)
+ source = JSON.generate(expected)
+ expected.delete_at(100)
- def string_deduplication_available?
- r1 = rand.to_s
- r2 = r1.dup
- begin
- (-r1).equal?(-r2)
- rescue NoMethodError
- false # No String#-@
+ fake_decimal_class = Class.new
+ fake_decimal_class.define_method(:initialize) do |number|
+ source.tr!('1', '0')
+ number.to_f
end
+
+ actual = JSON.parse(source, decimal_class: fake_decimal_class)
+ actual.delete_at(100)
+ assert_equal expected, actual
end
+ private
+
def assert_equal_float(expected, actual, delta = 1e-2)
Array === expected and expected = expected.first
Array === actual and actual = actual.first