1#!/usr/bin/env ruby
2# encoding: utf-8
3
4require 'test/unit'
5require File.join(File.dirname(__FILE__), 'setup_variant')
6
7class TestJSONGenerate < Test::Unit::TestCase
8  include JSON
9
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
24{
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
39}
40EOT
41  end
42
43  def test_generate
44    json = generate(@hash)
45    assert_equal(JSON.parse(@json2), JSON.parse(json))
46    json = JSON[@hash]
47    assert_equal(JSON.parse(@json2), JSON.parse(json))
48    parsed_json = parse(json)
49    assert_equal(@hash, parsed_json)
50    json = generate({1=>2})
51    assert_equal('{"1":2}', json)
52    parsed_json = parse(json)
53    assert_equal({"1"=>2}, parsed_json)
54    assert_raise(GeneratorError) { generate(666) }
55    assert_equal '666', generate(666, :quirks_mode => true)
56  end
57
58  def test_generate_pretty
59    json = pretty_generate(@hash)
60    # hashes aren't (insertion) ordered on every ruby implementation assert_equal(@json3, json)
61    assert_equal(JSON.parse(@json3), JSON.parse(json))
62    parsed_json = parse(json)
63    assert_equal(@hash, parsed_json)
64    json = pretty_generate({1=>2})
65    assert_equal(<<'EOT'.chomp, json)
66{
67  "1": 2
68}
69EOT
70    parsed_json = parse(json)
71    assert_equal({"1"=>2}, parsed_json)
72    assert_raise(GeneratorError) { pretty_generate(666) }
73    assert_equal '666', pretty_generate(666, :quirks_mode => true)
74  end
75
76  def test_fast_generate
77    json = fast_generate(@hash)
78    assert_equal(JSON.parse(@json2), JSON.parse(json))
79    parsed_json = parse(json)
80    assert_equal(@hash, parsed_json)
81    json = fast_generate({1=>2})
82    assert_equal('{"1":2}', json)
83    parsed_json = parse(json)
84    assert_equal({"1"=>2}, parsed_json)
85    assert_raise(GeneratorError) { fast_generate(666) }
86    assert_equal '666', fast_generate(666, :quirks_mode => true)
87  end
88
89  def test_own_state
90    state = State.new
91    json = generate(@hash, state)
92    assert_equal(JSON.parse(@json2), JSON.parse(json))
93    parsed_json = parse(json)
94    assert_equal(@hash, parsed_json)
95    json = generate({1=>2}, state)
96    assert_equal('{"1":2}', json)
97    parsed_json = parse(json)
98    assert_equal({"1"=>2}, parsed_json)
99    assert_raise(GeneratorError) { generate(666, state) }
100    state.quirks_mode = true
101    assert state.quirks_mode?
102    assert_equal '666', generate(666, state)
103  end
104
105  def test_states
106    json = generate({1=>2}, nil)
107    assert_equal('{"1":2}', json)
108    s = JSON.state.new
109    assert s.check_circular?
110    assert s[:check_circular?]
111    h = { 1=>2 }
112    h[3] = h
113    assert_raises(JSON::NestingError) {  generate(h) }
114    assert_raises(JSON::NestingError) {  generate(h, s) }
115    s = JSON.state.new
116    a = [ 1, 2 ]
117    a << a
118    assert_raises(JSON::NestingError) {  generate(a, s) }
119    assert s.check_circular?
120    assert s[:check_circular?]
121  end
122
123  def test_pretty_state
124    state = PRETTY_STATE_PROTOTYPE.dup
125    assert_equal({
126      :allow_nan             => false,
127      :array_nl              => "\n",
128      :ascii_only            => false,
129      :buffer_initial_length => 1024,
130      :quirks_mode           => false,
131      :depth                 => 0,
132      :indent                => "  ",
133      :max_nesting           => 100,
134      :object_nl             => "\n",
135      :space                 => " ",
136      :space_before          => "",
137    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
138  end
139
140  def test_safe_state
141    state = SAFE_STATE_PROTOTYPE.dup
142    assert_equal({
143      :allow_nan             => false,
144      :array_nl              => "",
145      :ascii_only            => false,
146      :buffer_initial_length => 1024,
147      :quirks_mode           => false,
148      :depth                 => 0,
149      :indent                => "",
150      :max_nesting           => 100,
151      :object_nl             => "",
152      :space                 => "",
153      :space_before          => "",
154    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
155  end
156
157  def test_fast_state
158    state = FAST_STATE_PROTOTYPE.dup
159    assert_equal({
160      :allow_nan             => false,
161      :array_nl              => "",
162      :ascii_only            => false,
163      :buffer_initial_length => 1024,
164      :quirks_mode           => false,
165      :depth                 => 0,
166      :indent                => "",
167      :max_nesting           => 0,
168      :object_nl             => "",
169      :space                 => "",
170      :space_before          => "",
171    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
172  end
173
174  def test_allow_nan
175    assert_raises(GeneratorError) { generate([JSON::NaN]) }
176    assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
177    assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
178    assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
179    assert_equal "[\n  NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
180    assert_raises(GeneratorError) { generate([JSON::Infinity]) }
181    assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
182    assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
183    assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
184    assert_equal "[\n  Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
185    assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
186    assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
187    assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
188    assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
189    assert_equal "[\n  -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
190  end
191
192  def test_depth
193    ary = []; ary << ary
194    assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
195    assert_raises(JSON::NestingError) { JSON.generate(ary) }
196    assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
197    assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
198    assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) }
199    assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
200    s = JSON.state.new
201    assert_equal 0, s.depth
202    assert_raises(JSON::NestingError) { ary.to_json(s) }
203    assert_equal 100, s.depth
204  end
205
206  def test_buffer_initial_length
207    s = JSON.state.new
208    assert_equal 1024, s.buffer_initial_length
209    s.buffer_initial_length = 0
210    assert_equal 1024, s.buffer_initial_length
211    s.buffer_initial_length = -1
212    assert_equal 1024, s.buffer_initial_length
213    s.buffer_initial_length = 128
214    assert_equal 128, s.buffer_initial_length
215  end
216
217  def test_gc
218    require_relative '../ruby/envutil.rb'
219    assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
220      bignum_too_long_to_embed_as_string = 1234567890123456789012345
221      expect = bignum_too_long_to_embed_as_string.to_s
222      GC.stress = true
223
224      10.times do |i|
225        tmp = bignum_too_long_to_embed_as_string.to_json
226        raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
227      end
228    EOS
229  end if GC.respond_to?(:stress=)
230
231  def test_configure_using_configure_and_merge
232    numbered_state = {
233      :indent       => "1",
234      :space        => '2',
235      :space_before => '3',
236      :object_nl    => '4',
237      :array_nl     => '5'
238    }
239    state1 = JSON.state.new
240    state1.merge(numbered_state)
241    assert_equal '1', state1.indent
242    assert_equal '2', state1.space
243    assert_equal '3', state1.space_before
244    assert_equal '4', state1.object_nl
245    assert_equal '5', state1.array_nl
246    state2 = JSON.state.new
247    state2.configure(numbered_state)
248    assert_equal '1', state2.indent
249    assert_equal '2', state2.space
250    assert_equal '3', state2.space_before
251    assert_equal '4', state2.object_nl
252    assert_equal '5', state2.array_nl
253  end
254
255  if defined?(JSON::Ext::Generator)
256    def test_broken_bignum # [ruby-core:38867]
257      pid = fork do
258        Bignum.class_eval do
259          def to_s
260          end
261        end
262        begin
263          JSON::Ext::Generator::State.new.generate(1<<64)
264          exit 1
265        rescue TypeError
266          exit 0
267        end
268      end
269      _, status = Process.waitpid2(pid)
270      assert status.success?
271    rescue NotImplementedError
272      # forking to avoid modifying core class of a parent process and
273      # introducing race conditions of tests are run in parallel
274    end
275  end
276
277  def test_hash_likeness_set_symbol
278    state = JSON.state.new
279    assert_equal nil, state[:foo]
280    assert_equal nil.class, state[:foo].class
281    assert_equal nil, state['foo']
282    state[:foo] = :bar
283    assert_equal :bar, state[:foo]
284    assert_equal :bar, state['foo']
285    state_hash = state.to_hash
286    assert_kind_of Hash, state_hash
287    assert_equal :bar, state_hash[:foo]
288  end
289
290  def test_hash_likeness_set_string
291    state = JSON.state.new
292    assert_equal nil, state[:foo]
293    assert_equal nil, state['foo']
294    state['foo'] = :bar
295    assert_equal :bar, state[:foo]
296    assert_equal :bar, state['foo']
297    state_hash = state.to_hash
298    assert_kind_of Hash, state_hash
299    assert_equal :bar, state_hash[:foo]
300  end
301end
302