* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / test / json / json_generator_test.rb
blobf31b6b290e5f2a693b9cbf345f09576050ad51c5
1 #!/usr/bin/env ruby
2 # encoding: utf-8
3 # frozen_string_literal: false
5 require 'test_helper'
7 class JSONGeneratorTest < Test::Unit::TestCase
8   include JSON
10   def setup
11     @hash = {
12       'a' => 2,
13       'b' => 3.141,
14       'c' => 'c',
15       'd' => [ 1, "b", 3.14 ],
16       'e' => { 'foo' => 'bar' },
17       'g' => "\"\0\037",
18       'h' => 1000.0,
19       'i' => 0.001
20     }
21     @json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
22       '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
23     @json3 = <<'EOT'.chomp
25   "a": 2,
26   "b": 3.141,
27   "c": "c",
28   "d": [
29     1,
30     "b",
31     3.14
32   ],
33   "e": {
34     "foo": "bar"
35   },
36   "g": "\"\u0000\u001f",
37   "h": 1000.0,
38   "i": 0.001
40 EOT
41   end
43   def silence
44     v = $VERBOSE
45     $VERBOSE = nil
46     yield
47   ensure
48     $VERBOSE = v
49   end
51   def test_generate
52     json = generate(@hash)
53     assert_equal(parse(@json2), parse(json))
54     json = JSON[@hash]
55     assert_equal(parse(@json2), parse(json))
56     parsed_json = parse(json)
57     assert_equal(@hash, parsed_json)
58     json = generate({1=>2})
59     assert_equal('{"1":2}', json)
60     parsed_json = parse(json)
61     assert_equal({"1"=>2}, parsed_json)
62     assert_equal '666', generate(666)
63   end
65   def test_generate_pretty
66     json = pretty_generate({})
67     assert_equal(<<'EOT'.chomp, json)
70 EOT
71     json = pretty_generate(@hash)
72     # hashes aren't (insertion) ordered on every ruby implementation
73     # assert_equal(@json3, json)
74     assert_equal(parse(@json3), parse(json))
75     parsed_json = parse(json)
76     assert_equal(@hash, parsed_json)
77     json = pretty_generate({1=>2})
78     assert_equal(<<'EOT'.chomp, json)
80   "1": 2
82 EOT
83     parsed_json = parse(json)
84     assert_equal({"1"=>2}, parsed_json)
85     assert_equal '666', pretty_generate(666)
86   end
88   def test_generate_custom
89     state = State.new(:space_before => " ", :space => "   ", :indent => "<i>", :object_nl => "\n", :array_nl => "<a_nl>")
90     json = generate({1=>{2=>3,4=>[5,6]}}, state)
91     assert_equal(<<'EOT'.chomp, json)
93 <i>"1" :   {
94 <i><i>"2" :   3,
95 <i><i>"4" :   [<a_nl><i><i><i>5,<a_nl><i><i><i>6<a_nl><i><i>]
96 <i>}
98 EOT
99   end
101   def test_fast_generate
102     json = fast_generate(@hash)
103     assert_equal(parse(@json2), parse(json))
104     parsed_json = parse(json)
105     assert_equal(@hash, parsed_json)
106     json = fast_generate({1=>2})
107     assert_equal('{"1":2}', json)
108     parsed_json = parse(json)
109     assert_equal({"1"=>2}, parsed_json)
110     assert_equal '666', fast_generate(666)
111   end
113   def test_own_state
114     state = State.new
115     json = generate(@hash, state)
116     assert_equal(parse(@json2), parse(json))
117     parsed_json = parse(json)
118     assert_equal(@hash, parsed_json)
119     json = generate({1=>2}, state)
120     assert_equal('{"1":2}', json)
121     parsed_json = parse(json)
122     assert_equal({"1"=>2}, parsed_json)
123     assert_equal '666', generate(666, state)
124   end
126   def test_states
127     json = generate({1=>2}, nil)
128     assert_equal('{"1":2}', json)
129     s = JSON.state.new
130     assert s.check_circular?
131     assert s[:check_circular?]
132     h = { 1=>2 }
133     h[3] = h
134     assert_raise(JSON::NestingError) {  generate(h) }
135     assert_raise(JSON::NestingError) {  generate(h, s) }
136     s = JSON.state.new
137     a = [ 1, 2 ]
138     a << a
139     assert_raise(JSON::NestingError) {  generate(a, s) }
140     assert s.check_circular?
141     assert s[:check_circular?]
142   end
144   def test_pretty_state
145     state = JSON.create_pretty_state
146     assert_equal({
147       :allow_nan             => false,
148       :array_nl              => "\n",
149       :ascii_only            => false,
150       :buffer_initial_length => 1024,
151       :depth                 => 0,
152       :escape_slash          => false,
153       :indent                => "  ",
154       :max_nesting           => 100,
155       :object_nl             => "\n",
156       :space                 => " ",
157       :space_before          => "",
158     }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
159   end
161   def test_safe_state
162     state = JSON::State.new
163     assert_equal({
164       :allow_nan             => false,
165       :array_nl              => "",
166       :ascii_only            => false,
167       :buffer_initial_length => 1024,
168       :depth                 => 0,
169       :escape_slash          => false,
170       :indent                => "",
171       :max_nesting           => 100,
172       :object_nl             => "",
173       :space                 => "",
174       :space_before          => "",
175     }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
176   end
178   def test_fast_state
179     state = JSON.create_fast_state
180     assert_equal({
181       :allow_nan             => false,
182       :array_nl              => "",
183       :ascii_only            => false,
184       :buffer_initial_length => 1024,
185       :depth                 => 0,
186       :escape_slash          => false,
187       :indent                => "",
188       :max_nesting           => 0,
189       :object_nl             => "",
190       :space                 => "",
191       :space_before          => "",
192     }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
193   end
195   def test_allow_nan
196     assert_raise(GeneratorError) { generate([JSON::NaN]) }
197     assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
198     assert_raise(GeneratorError) { fast_generate([JSON::NaN]) }
199     assert_raise(GeneratorError) { pretty_generate([JSON::NaN]) }
200     assert_equal "[\n  NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
201     assert_raise(GeneratorError) { generate([JSON::Infinity]) }
202     assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
203     assert_raise(GeneratorError) { fast_generate([JSON::Infinity]) }
204     assert_raise(GeneratorError) { pretty_generate([JSON::Infinity]) }
205     assert_equal "[\n  Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
206     assert_raise(GeneratorError) { generate([JSON::MinusInfinity]) }
207     assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
208     assert_raise(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
209     assert_raise(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
210     assert_equal "[\n  -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
211   end
213   def test_depth
214     ary = []; ary << ary
215     assert_raise(JSON::NestingError) { generate(ary) }
216     assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) }
217     s = JSON.state.new
218     assert_equal 0, s.depth
219     assert_raise(JSON::NestingError) { ary.to_json(s) }
220     assert_equal 100, s.depth
221   end
223   def test_buffer_initial_length
224     s = JSON.state.new
225     assert_equal 1024, s.buffer_initial_length
226     s.buffer_initial_length = 0
227     assert_equal 1024, s.buffer_initial_length
228     s.buffer_initial_length = -1
229     assert_equal 1024, s.buffer_initial_length
230     s.buffer_initial_length = 128
231     assert_equal 128, s.buffer_initial_length
232   end
234   def test_gc
235     if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/)
236       assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
237         bignum_too_long_to_embed_as_string = 1234567890123456789012345
238         expect = bignum_too_long_to_embed_as_string.to_s
239         GC.stress = true
241         10.times do |i|
242           tmp = bignum_too_long_to_embed_as_string.to_json
243           raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
244         end
245       EOS
246     end
247   end if GC.respond_to?(:stress=)
249   def test_configure_using_configure_and_merge
250     numbered_state = {
251       :indent       => "1",
252       :space        => '2',
253       :space_before => '3',
254       :object_nl    => '4',
255       :array_nl     => '5'
256     }
257     state1 = JSON.state.new
258     state1.merge(numbered_state)
259     assert_equal '1', state1.indent
260     assert_equal '2', state1.space
261     assert_equal '3', state1.space_before
262     assert_equal '4', state1.object_nl
263     assert_equal '5', state1.array_nl
264     state2 = JSON.state.new
265     state2.configure(numbered_state)
266     assert_equal '1', state2.indent
267     assert_equal '2', state2.space
268     assert_equal '3', state2.space_before
269     assert_equal '4', state2.object_nl
270     assert_equal '5', state2.array_nl
271   end
273   def test_configure_hash_conversion
274     state = JSON.state.new
275     state.configure(:indent => '1')
276     assert_equal '1', state.indent
277     state = JSON.state.new
278     foo = 'foo'
279     assert_raise(TypeError) do
280       state.configure(foo)
281     end
282     def foo.to_h
283       { :indent => '2' }
284     end
285     state.configure(foo)
286     assert_equal '2', state.indent
287   end
289   if defined?(JSON::Ext::Generator)
290     def test_broken_bignum # [ruby-core:38867]
291       pid = fork do
292         x = 1 << 64
293         x.class.class_eval do
294           def to_s
295           end
296         end
297         begin
298           JSON::Ext::Generator::State.new.generate(x)
299           exit 1
300         rescue TypeError
301           exit 0
302         end
303       end
304       _, status = Process.waitpid2(pid)
305       assert status.success?
306     rescue NotImplementedError
307       # forking to avoid modifying core class of a parent process and
308       # introducing race conditions of tests are run in parallel
309     end
310   end
312   def test_hash_likeness_set_symbol
313     state = JSON.state.new
314     assert_equal nil, state[:foo]
315     assert_equal nil.class, state[:foo].class
316     assert_equal nil, state['foo']
317     state[:foo] = :bar
318     assert_equal :bar, state[:foo]
319     assert_equal :bar, state['foo']
320     state_hash = state.to_hash
321     assert_kind_of Hash, state_hash
322     assert_equal :bar, state_hash[:foo]
323   end
325   def test_hash_likeness_set_string
326     state = JSON.state.new
327     assert_equal nil, state[:foo]
328     assert_equal nil, state['foo']
329     state['foo'] = :bar
330     assert_equal :bar, state[:foo]
331     assert_equal :bar, state['foo']
332     state_hash = state.to_hash
333     assert_kind_of Hash, state_hash
334     assert_equal :bar, state_hash[:foo]
335   end
337   def test_json_generate
338     assert_raise JSON::GeneratorError do
339       assert_equal true, generate(["\xea"])
340     end
341   end
343   def test_nesting
344     too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
345     too_deep_ary = eval too_deep
346     assert_raise(JSON::NestingError) { generate too_deep_ary }
347     assert_raise(JSON::NestingError) { generate too_deep_ary, :max_nesting => 100 }
348     ok = generate too_deep_ary, :max_nesting => 101
349     assert_equal too_deep, ok
350     ok = generate too_deep_ary, :max_nesting => nil
351     assert_equal too_deep, ok
352     ok = generate too_deep_ary, :max_nesting => false
353     assert_equal too_deep, ok
354     ok = generate too_deep_ary, :max_nesting => 0
355     assert_equal too_deep, ok
356   end
358   def test_backslash
359     data = [ '\\.(?i:gif|jpe?g|png)$' ]
360     json = '["\\\\.(?i:gif|jpe?g|png)$"]'
361     assert_equal json, generate(data)
362     #
363     data = [ '\\"' ]
364     json = '["\\\\\""]'
365     assert_equal json, generate(data)
366     #
367     data = [ '/' ]
368     json = '["/"]'
369     assert_equal json, generate(data)
370     #
371     data = [ '/' ]
372     json = '["\/"]'
373     assert_equal json, generate(data, :escape_slash => true)
374     #
375     data = ['"']
376     json = '["\""]'
377     assert_equal json, generate(data)
378     #
379     data = ["'"]
380     json = '["\\\'"]'
381     assert_equal '["\'"]', generate(data)
382   end
384   def test_string_subclass
385     s = Class.new(String) do
386       def to_s; self; end
387       undef to_json
388     end
389     assert_nothing_raised(SystemStackError) do
390       assert_equal '["foo"]', JSON.generate([s.new('foo')])
391     end
392   end
394   if defined?(Encoding)
395     def test_nonutf8_encoding
396       assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json)
397     end
398   end