2015-04-12 04:36:37 -04:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
# encoding: utf-8
|
2015-12-16 00:07:31 -05:00
|
|
|
# frozen_string_literal: false
|
2015-04-12 04:36:37 -04:00
|
|
|
|
2016-07-05 07:49:39 -04:00
|
|
|
require 'test_helper'
|
2015-04-12 04:36:37 -04:00
|
|
|
|
2016-07-13 09:27:07 -04:00
|
|
|
class JSONGeneratorTest < Test::Unit::TestCase
|
2015-04-12 04:36:37 -04:00
|
|
|
include JSON
|
|
|
|
|
|
|
|
def setup
|
|
|
|
@hash = {
|
|
|
|
'a' => 2,
|
|
|
|
'b' => 3.141,
|
|
|
|
'c' => 'c',
|
|
|
|
'd' => [ 1, "b", 3.14 ],
|
|
|
|
'e' => { 'foo' => 'bar' },
|
|
|
|
'g' => "\"\0\037",
|
|
|
|
'h' => 1000.0,
|
|
|
|
'i' => 0.001
|
|
|
|
}
|
|
|
|
@json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
|
|
|
|
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
|
|
|
|
@json3 = <<'EOT'.chomp
|
|
|
|
{
|
|
|
|
"a": 2,
|
|
|
|
"b": 3.141,
|
|
|
|
"c": "c",
|
|
|
|
"d": [
|
|
|
|
1,
|
|
|
|
"b",
|
|
|
|
3.14
|
|
|
|
],
|
|
|
|
"e": {
|
|
|
|
"foo": "bar"
|
|
|
|
},
|
|
|
|
"g": "\"\u0000\u001f",
|
|
|
|
"h": 1000.0,
|
|
|
|
"i": 0.001
|
|
|
|
}
|
|
|
|
EOT
|
|
|
|
end
|
|
|
|
|
2019-10-17 16:30:09 -04:00
|
|
|
def silence
|
|
|
|
v = $VERBOSE
|
|
|
|
$VERBOSE = nil
|
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
$VERBOSE = v
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_remove_const_segv
|
|
|
|
stress = GC.stress
|
|
|
|
const = JSON::SAFE_STATE_PROTOTYPE.dup
|
|
|
|
|
|
|
|
bignum_too_long_to_embed_as_string = 1234567890123456789012345
|
|
|
|
expect = bignum_too_long_to_embed_as_string.to_s
|
|
|
|
GC.stress = true
|
|
|
|
|
|
|
|
10.times do |i|
|
|
|
|
tmp = bignum_too_long_to_embed_as_string.to_json
|
|
|
|
raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
|
|
|
|
end
|
|
|
|
|
|
|
|
silence do
|
|
|
|
JSON.const_set :SAFE_STATE_PROTOTYPE, nil
|
|
|
|
end
|
|
|
|
|
|
|
|
10.times do |i|
|
|
|
|
assert_raise TypeError do
|
|
|
|
bignum_too_long_to_embed_as_string.to_json
|
|
|
|
end
|
|
|
|
end
|
|
|
|
ensure
|
|
|
|
GC.stress = stress
|
|
|
|
silence do
|
|
|
|
JSON.const_set :SAFE_STATE_PROTOTYPE, const
|
|
|
|
end
|
2020-07-06 14:09:50 -04:00
|
|
|
end if JSON.const_defined?("Ext") && RUBY_ENGINE != 'jruby'
|
2019-10-17 16:30:09 -04:00
|
|
|
|
2015-04-12 04:36:37 -04:00
|
|
|
def test_generate
|
|
|
|
json = generate(@hash)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal(parse(@json2), parse(json))
|
2015-04-12 04:36:37 -04:00
|
|
|
json = JSON[@hash]
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal(parse(@json2), parse(json))
|
2015-04-12 04:36:37 -04:00
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal(@hash, parsed_json)
|
|
|
|
json = generate({1=>2})
|
|
|
|
assert_equal('{"1":2}', json)
|
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal({"1"=>2}, parsed_json)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal '666', generate(666)
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_generate_pretty
|
2020-10-06 22:13:48 -04:00
|
|
|
json = pretty_generate({})
|
|
|
|
assert_equal(<<'EOT'.chomp, json)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
EOT
|
2015-04-12 04:36:37 -04:00
|
|
|
json = pretty_generate(@hash)
|
2016-07-05 07:49:39 -04:00
|
|
|
# hashes aren't (insertion) ordered on every ruby implementation
|
|
|
|
# assert_equal(@json3, json)
|
|
|
|
assert_equal(parse(@json3), parse(json))
|
2015-04-12 04:36:37 -04:00
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal(@hash, parsed_json)
|
|
|
|
json = pretty_generate({1=>2})
|
|
|
|
assert_equal(<<'EOT'.chomp, json)
|
|
|
|
{
|
|
|
|
"1": 2
|
|
|
|
}
|
|
|
|
EOT
|
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal({"1"=>2}, parsed_json)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal '666', pretty_generate(666)
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_generate_custom
|
|
|
|
state = State.new(:space_before => " ", :space => " ", :indent => "<i>", :object_nl => "\n", :array_nl => "<a_nl>")
|
|
|
|
json = generate({1=>{2=>3,4=>[5,6]}}, state)
|
|
|
|
assert_equal(<<'EOT'.chomp, json)
|
|
|
|
{
|
|
|
|
<i>"1" : {
|
|
|
|
<i><i>"2" : 3,
|
|
|
|
<i><i>"4" : [<a_nl><i><i><i>5,<a_nl><i><i><i>6<a_nl><i><i>]
|
|
|
|
<i>}
|
|
|
|
}
|
|
|
|
EOT
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_fast_generate
|
|
|
|
json = fast_generate(@hash)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal(parse(@json2), parse(json))
|
2015-04-12 04:36:37 -04:00
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal(@hash, parsed_json)
|
|
|
|
json = fast_generate({1=>2})
|
|
|
|
assert_equal('{"1":2}', json)
|
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal({"1"=>2}, parsed_json)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal '666', fast_generate(666)
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_own_state
|
|
|
|
state = State.new
|
|
|
|
json = generate(@hash, state)
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal(parse(@json2), parse(json))
|
2015-04-12 04:36:37 -04:00
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal(@hash, parsed_json)
|
|
|
|
json = generate({1=>2}, state)
|
|
|
|
assert_equal('{"1":2}', json)
|
|
|
|
parsed_json = parse(json)
|
|
|
|
assert_equal({"1"=>2}, parsed_json)
|
|
|
|
assert_equal '666', generate(666, state)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_states
|
|
|
|
json = generate({1=>2}, nil)
|
|
|
|
assert_equal('{"1":2}', json)
|
|
|
|
s = JSON.state.new
|
|
|
|
assert s.check_circular?
|
|
|
|
assert s[:check_circular?]
|
|
|
|
h = { 1=>2 }
|
|
|
|
h[3] = h
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(JSON::NestingError) { generate(h) }
|
|
|
|
assert_raise(JSON::NestingError) { generate(h, s) }
|
2015-04-12 04:36:37 -04:00
|
|
|
s = JSON.state.new
|
|
|
|
a = [ 1, 2 ]
|
|
|
|
a << a
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(JSON::NestingError) { generate(a, s) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert s.check_circular?
|
|
|
|
assert s[:check_circular?]
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_pretty_state
|
|
|
|
state = PRETTY_STATE_PROTOTYPE.dup
|
|
|
|
assert_equal({
|
|
|
|
:allow_nan => false,
|
|
|
|
:array_nl => "\n",
|
|
|
|
:ascii_only => false,
|
|
|
|
:buffer_initial_length => 1024,
|
|
|
|
:depth => 0,
|
2020-01-30 06:48:05 -05:00
|
|
|
:escape_slash => false,
|
2015-04-12 04:36:37 -04:00
|
|
|
:indent => " ",
|
|
|
|
:max_nesting => 100,
|
|
|
|
:object_nl => "\n",
|
|
|
|
:space => " ",
|
|
|
|
:space_before => "",
|
|
|
|
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_safe_state
|
|
|
|
state = SAFE_STATE_PROTOTYPE.dup
|
|
|
|
assert_equal({
|
|
|
|
:allow_nan => false,
|
|
|
|
:array_nl => "",
|
|
|
|
:ascii_only => false,
|
|
|
|
:buffer_initial_length => 1024,
|
|
|
|
:depth => 0,
|
2020-01-30 06:48:05 -05:00
|
|
|
:escape_slash => false,
|
2015-04-12 04:36:37 -04:00
|
|
|
:indent => "",
|
|
|
|
:max_nesting => 100,
|
|
|
|
:object_nl => "",
|
|
|
|
:space => "",
|
|
|
|
:space_before => "",
|
|
|
|
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_fast_state
|
|
|
|
state = FAST_STATE_PROTOTYPE.dup
|
|
|
|
assert_equal({
|
|
|
|
:allow_nan => false,
|
|
|
|
:array_nl => "",
|
|
|
|
:ascii_only => false,
|
|
|
|
:buffer_initial_length => 1024,
|
|
|
|
:depth => 0,
|
2020-01-30 06:48:05 -05:00
|
|
|
:escape_slash => false,
|
2015-04-12 04:36:37 -04:00
|
|
|
:indent => "",
|
|
|
|
:max_nesting => 0,
|
|
|
|
:object_nl => "",
|
|
|
|
:space => "",
|
|
|
|
:space_before => "",
|
|
|
|
}.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_allow_nan
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { generate([JSON::NaN]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { fast_generate([JSON::NaN]) }
|
|
|
|
assert_raise(GeneratorError) { pretty_generate([JSON::NaN]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal "[\n NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { generate([JSON::Infinity]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { fast_generate([JSON::Infinity]) }
|
|
|
|
assert_raise(GeneratorError) { pretty_generate([JSON::Infinity]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal "[\n Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { generate([JSON::MinusInfinity]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
|
|
|
|
assert_raise(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal "[\n -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_depth
|
|
|
|
ary = []; ary << ary
|
|
|
|
assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_raise(JSON::NestingError) { generate(ary) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
|
|
|
|
assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
|
|
|
|
s = JSON.state.new
|
|
|
|
assert_equal 0, s.depth
|
2015-10-30 00:37:13 -04:00
|
|
|
assert_raise(JSON::NestingError) { ary.to_json(s) }
|
2015-04-12 04:36:37 -04:00
|
|
|
assert_equal 100, s.depth
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_buffer_initial_length
|
|
|
|
s = JSON.state.new
|
|
|
|
assert_equal 1024, s.buffer_initial_length
|
|
|
|
s.buffer_initial_length = 0
|
|
|
|
assert_equal 1024, s.buffer_initial_length
|
|
|
|
s.buffer_initial_length = -1
|
|
|
|
assert_equal 1024, s.buffer_initial_length
|
|
|
|
s.buffer_initial_length = 128
|
|
|
|
assert_equal 128, s.buffer_initial_length
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_gc
|
|
|
|
if respond_to?(:assert_in_out_err)
|
|
|
|
assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
|
|
|
|
bignum_too_long_to_embed_as_string = 1234567890123456789012345
|
|
|
|
expect = bignum_too_long_to_embed_as_string.to_s
|
|
|
|
GC.stress = true
|
|
|
|
|
|
|
|
10.times do |i|
|
|
|
|
tmp = bignum_too_long_to_embed_as_string.to_json
|
|
|
|
raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
|
|
|
|
end
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end if GC.respond_to?(:stress=)
|
|
|
|
|
|
|
|
def test_configure_using_configure_and_merge
|
|
|
|
numbered_state = {
|
|
|
|
:indent => "1",
|
|
|
|
:space => '2',
|
|
|
|
:space_before => '3',
|
|
|
|
:object_nl => '4',
|
|
|
|
:array_nl => '5'
|
|
|
|
}
|
|
|
|
state1 = JSON.state.new
|
|
|
|
state1.merge(numbered_state)
|
|
|
|
assert_equal '1', state1.indent
|
|
|
|
assert_equal '2', state1.space
|
|
|
|
assert_equal '3', state1.space_before
|
|
|
|
assert_equal '4', state1.object_nl
|
|
|
|
assert_equal '5', state1.array_nl
|
|
|
|
state2 = JSON.state.new
|
|
|
|
state2.configure(numbered_state)
|
|
|
|
assert_equal '1', state2.indent
|
|
|
|
assert_equal '2', state2.space
|
|
|
|
assert_equal '3', state2.space_before
|
|
|
|
assert_equal '4', state2.object_nl
|
|
|
|
assert_equal '5', state2.array_nl
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_configure_hash_conversion
|
|
|
|
state = JSON.state.new
|
|
|
|
state.configure(:indent => '1')
|
|
|
|
assert_equal '1', state.indent
|
|
|
|
state = JSON.state.new
|
|
|
|
foo = 'foo'
|
|
|
|
assert_raise(TypeError) do
|
|
|
|
state.configure(foo)
|
|
|
|
end
|
|
|
|
def foo.to_h
|
|
|
|
{ :indent => '2' }
|
|
|
|
end
|
|
|
|
state.configure(foo)
|
|
|
|
assert_equal '2', state.indent
|
|
|
|
end
|
|
|
|
|
|
|
|
if defined?(JSON::Ext::Generator)
|
|
|
|
def test_broken_bignum # [ruby-core:38867]
|
|
|
|
pid = fork do
|
2016-09-09 05:27:38 -04:00
|
|
|
x = 1 << 64
|
|
|
|
x.class.class_eval do
|
2015-04-12 04:36:37 -04:00
|
|
|
def to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
begin
|
2016-09-09 05:27:38 -04:00
|
|
|
JSON::Ext::Generator::State.new.generate(x)
|
2015-04-12 04:36:37 -04:00
|
|
|
exit 1
|
|
|
|
rescue TypeError
|
|
|
|
exit 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
_, status = Process.waitpid2(pid)
|
|
|
|
assert status.success?
|
|
|
|
rescue NotImplementedError
|
|
|
|
# forking to avoid modifying core class of a parent process and
|
|
|
|
# introducing race conditions of tests are run in parallel
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_hash_likeness_set_symbol
|
|
|
|
state = JSON.state.new
|
|
|
|
assert_equal nil, state[:foo]
|
|
|
|
assert_equal nil.class, state[:foo].class
|
|
|
|
assert_equal nil, state['foo']
|
|
|
|
state[:foo] = :bar
|
|
|
|
assert_equal :bar, state[:foo]
|
|
|
|
assert_equal :bar, state['foo']
|
|
|
|
state_hash = state.to_hash
|
|
|
|
assert_kind_of Hash, state_hash
|
|
|
|
assert_equal :bar, state_hash[:foo]
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_hash_likeness_set_string
|
|
|
|
state = JSON.state.new
|
|
|
|
assert_equal nil, state[:foo]
|
|
|
|
assert_equal nil, state['foo']
|
|
|
|
state['foo'] = :bar
|
|
|
|
assert_equal :bar, state[:foo]
|
|
|
|
assert_equal :bar, state['foo']
|
|
|
|
state_hash = state.to_hash
|
|
|
|
assert_kind_of Hash, state_hash
|
|
|
|
assert_equal :bar, state_hash[:foo]
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_json_generate
|
|
|
|
assert_raise JSON::GeneratorError do
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal true, generate(["\xea"])
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-05 07:49:39 -04:00
|
|
|
def test_nesting
|
|
|
|
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
|
|
|
too_deep_ary = eval too_deep
|
|
|
|
assert_raise(JSON::NestingError) { generate too_deep_ary }
|
|
|
|
assert_raise(JSON::NestingError) { generate too_deep_ary, :max_nesting => 100 }
|
|
|
|
ok = generate too_deep_ary, :max_nesting => 101
|
|
|
|
assert_equal too_deep, ok
|
|
|
|
ok = generate too_deep_ary, :max_nesting => nil
|
|
|
|
assert_equal too_deep, ok
|
|
|
|
ok = generate too_deep_ary, :max_nesting => false
|
|
|
|
assert_equal too_deep, ok
|
|
|
|
ok = generate too_deep_ary, :max_nesting => 0
|
|
|
|
assert_equal too_deep, ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_backslash
|
|
|
|
data = [ '\\.(?i:gif|jpe?g|png)$' ]
|
|
|
|
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
|
|
|
assert_equal json, generate(data)
|
|
|
|
#
|
|
|
|
data = [ '\\"' ]
|
|
|
|
json = '["\\\\\""]'
|
|
|
|
assert_equal json, generate(data)
|
|
|
|
#
|
|
|
|
data = [ '/' ]
|
|
|
|
json = '["/"]'
|
|
|
|
assert_equal json, generate(data)
|
|
|
|
#
|
2020-01-30 06:48:05 -05:00
|
|
|
data = [ '/' ]
|
|
|
|
json = '["\/"]'
|
|
|
|
assert_equal json, generate(data, :escape_slash => true)
|
|
|
|
#
|
2016-07-05 07:49:39 -04:00
|
|
|
data = ['"']
|
|
|
|
json = '["\""]'
|
|
|
|
assert_equal json, generate(data)
|
|
|
|
#
|
|
|
|
data = ["'"]
|
|
|
|
json = '["\\\'"]'
|
|
|
|
assert_equal '["\'"]', generate(data)
|
|
|
|
end
|
|
|
|
|
2015-04-12 04:36:37 -04:00
|
|
|
def test_string_subclass
|
|
|
|
s = Class.new(String) do
|
|
|
|
def to_s; self; end
|
|
|
|
undef to_json
|
|
|
|
end
|
|
|
|
assert_nothing_raised(SystemStackError) do
|
2016-07-05 07:49:39 -04:00
|
|
|
assert_equal '["foo"]', JSON.generate([s.new('foo')])
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|
|
|
|
end
|
2019-10-14 06:13:20 -04:00
|
|
|
|
|
|
|
if defined?(Encoding)
|
|
|
|
def test_nonutf8_encoding
|
|
|
|
assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json)
|
|
|
|
end
|
|
|
|
end
|
2015-04-12 04:36:37 -04:00
|
|
|
end
|