oj 3.10.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +104 -0
  4. data/ext/oj/buf.h +103 -0
  5. data/ext/oj/cache8.c +107 -0
  6. data/ext/oj/cache8.h +48 -0
  7. data/ext/oj/circarray.c +68 -0
  8. data/ext/oj/circarray.h +23 -0
  9. data/ext/oj/code.c +235 -0
  10. data/ext/oj/code.h +42 -0
  11. data/ext/oj/compat.c +299 -0
  12. data/ext/oj/custom.c +1218 -0
  13. data/ext/oj/dump.c +1249 -0
  14. data/ext/oj/dump.h +96 -0
  15. data/ext/oj/dump_compat.c +975 -0
  16. data/ext/oj/dump_leaf.c +252 -0
  17. data/ext/oj/dump_object.c +844 -0
  18. data/ext/oj/dump_strict.c +434 -0
  19. data/ext/oj/encode.h +45 -0
  20. data/ext/oj/err.c +57 -0
  21. data/ext/oj/err.h +70 -0
  22. data/ext/oj/extconf.rb +53 -0
  23. data/ext/oj/fast.c +1771 -0
  24. data/ext/oj/hash.c +163 -0
  25. data/ext/oj/hash.h +46 -0
  26. data/ext/oj/hash_test.c +512 -0
  27. data/ext/oj/mimic_json.c +890 -0
  28. data/ext/oj/object.c +775 -0
  29. data/ext/oj/odd.c +231 -0
  30. data/ext/oj/odd.h +44 -0
  31. data/ext/oj/oj.c +1723 -0
  32. data/ext/oj/oj.h +387 -0
  33. data/ext/oj/parse.c +1134 -0
  34. data/ext/oj/parse.h +112 -0
  35. data/ext/oj/rails.c +1528 -0
  36. data/ext/oj/rails.h +21 -0
  37. data/ext/oj/reader.c +231 -0
  38. data/ext/oj/reader.h +151 -0
  39. data/ext/oj/resolve.c +102 -0
  40. data/ext/oj/resolve.h +14 -0
  41. data/ext/oj/rxclass.c +147 -0
  42. data/ext/oj/rxclass.h +27 -0
  43. data/ext/oj/saj.c +714 -0
  44. data/ext/oj/scp.c +224 -0
  45. data/ext/oj/sparse.c +924 -0
  46. data/ext/oj/stream_writer.c +363 -0
  47. data/ext/oj/strict.c +212 -0
  48. data/ext/oj/string_writer.c +534 -0
  49. data/ext/oj/trace.c +79 -0
  50. data/ext/oj/trace.h +28 -0
  51. data/ext/oj/util.c +136 -0
  52. data/ext/oj/util.h +19 -0
  53. data/ext/oj/val_stack.c +118 -0
  54. data/ext/oj/val_stack.h +185 -0
  55. data/ext/oj/wab.c +631 -0
  56. data/lib/oj.rb +21 -0
  57. data/lib/oj/active_support_helper.rb +41 -0
  58. data/lib/oj/bag.rb +88 -0
  59. data/lib/oj/easy_hash.rb +52 -0
  60. data/lib/oj/error.rb +22 -0
  61. data/lib/oj/json.rb +176 -0
  62. data/lib/oj/mimic.rb +267 -0
  63. data/lib/oj/saj.rb +66 -0
  64. data/lib/oj/schandler.rb +142 -0
  65. data/lib/oj/state.rb +131 -0
  66. data/lib/oj/version.rb +5 -0
  67. data/pages/Advanced.md +22 -0
  68. data/pages/Compatibility.md +25 -0
  69. data/pages/Custom.md +23 -0
  70. data/pages/Encoding.md +65 -0
  71. data/pages/JsonGem.md +79 -0
  72. data/pages/Modes.md +155 -0
  73. data/pages/Options.md +287 -0
  74. data/pages/Rails.md +155 -0
  75. data/pages/Security.md +20 -0
  76. data/pages/WAB.md +13 -0
  77. data/test/_test_active.rb +76 -0
  78. data/test/_test_active_mimic.rb +96 -0
  79. data/test/_test_mimic_rails.rb +126 -0
  80. data/test/activerecord/result_test.rb +27 -0
  81. data/test/activesupport4/decoding_test.rb +108 -0
  82. data/test/activesupport4/encoding_test.rb +531 -0
  83. data/test/activesupport4/test_helper.rb +41 -0
  84. data/test/activesupport5/abstract_unit.rb +45 -0
  85. data/test/activesupport5/decoding_test.rb +133 -0
  86. data/test/activesupport5/encoding_test.rb +500 -0
  87. data/test/activesupport5/encoding_test_cases.rb +98 -0
  88. data/test/activesupport5/test_helper.rb +72 -0
  89. data/test/activesupport5/time_zone_test_helpers.rb +39 -0
  90. data/test/activesupport6/abstract_unit.rb +44 -0
  91. data/test/activesupport6/decoding_test.rb +133 -0
  92. data/test/activesupport6/encoding_test.rb +507 -0
  93. data/test/activesupport6/encoding_test_cases.rb +98 -0
  94. data/test/activesupport6/test_common.rb +17 -0
  95. data/test/activesupport6/test_helper.rb +163 -0
  96. data/test/activesupport6/time_zone_test_helpers.rb +39 -0
  97. data/test/bar.rb +35 -0
  98. data/test/baz.rb +16 -0
  99. data/test/files.rb +29 -0
  100. data/test/foo.rb +52 -0
  101. data/test/helper.rb +26 -0
  102. data/test/isolated/shared.rb +308 -0
  103. data/test/isolated/test_mimic_after.rb +13 -0
  104. data/test/isolated/test_mimic_alone.rb +12 -0
  105. data/test/isolated/test_mimic_as_json.rb +45 -0
  106. data/test/isolated/test_mimic_before.rb +13 -0
  107. data/test/isolated/test_mimic_define.rb +28 -0
  108. data/test/isolated/test_mimic_rails_after.rb +22 -0
  109. data/test/isolated/test_mimic_rails_before.rb +21 -0
  110. data/test/isolated/test_mimic_redefine.rb +15 -0
  111. data/test/json_gem/json_addition_test.rb +216 -0
  112. data/test/json_gem/json_common_interface_test.rb +148 -0
  113. data/test/json_gem/json_encoding_test.rb +107 -0
  114. data/test/json_gem/json_ext_parser_test.rb +20 -0
  115. data/test/json_gem/json_fixtures_test.rb +35 -0
  116. data/test/json_gem/json_generator_test.rb +383 -0
  117. data/test/json_gem/json_generic_object_test.rb +90 -0
  118. data/test/json_gem/json_parser_test.rb +470 -0
  119. data/test/json_gem/json_string_matching_test.rb +42 -0
  120. data/test/json_gem/test_helper.rb +18 -0
  121. data/test/perf.rb +107 -0
  122. data/test/perf_compat.rb +130 -0
  123. data/test/perf_fast.rb +164 -0
  124. data/test/perf_file.rb +64 -0
  125. data/test/perf_object.rb +138 -0
  126. data/test/perf_saj.rb +109 -0
  127. data/test/perf_scp.rb +151 -0
  128. data/test/perf_simple.rb +287 -0
  129. data/test/perf_strict.rb +145 -0
  130. data/test/perf_wab.rb +131 -0
  131. data/test/prec.rb +23 -0
  132. data/test/sample.rb +54 -0
  133. data/test/sample/change.rb +14 -0
  134. data/test/sample/dir.rb +19 -0
  135. data/test/sample/doc.rb +36 -0
  136. data/test/sample/file.rb +48 -0
  137. data/test/sample/group.rb +16 -0
  138. data/test/sample/hasprops.rb +16 -0
  139. data/test/sample/layer.rb +12 -0
  140. data/test/sample/line.rb +20 -0
  141. data/test/sample/oval.rb +10 -0
  142. data/test/sample/rect.rb +10 -0
  143. data/test/sample/shape.rb +35 -0
  144. data/test/sample/text.rb +20 -0
  145. data/test/sample_json.rb +37 -0
  146. data/test/test_compat.rb +502 -0
  147. data/test/test_custom.rb +527 -0
  148. data/test/test_debian.rb +53 -0
  149. data/test/test_fast.rb +470 -0
  150. data/test/test_file.rb +239 -0
  151. data/test/test_gc.rb +49 -0
  152. data/test/test_hash.rb +29 -0
  153. data/test/test_integer_range.rb +72 -0
  154. data/test/test_null.rb +376 -0
  155. data/test/test_object.rb +1027 -0
  156. data/test/test_rails.rb +26 -0
  157. data/test/test_saj.rb +186 -0
  158. data/test/test_scp.rb +433 -0
  159. data/test/test_strict.rb +433 -0
  160. data/test/test_various.rb +719 -0
  161. data/test/test_wab.rb +307 -0
  162. data/test/test_writer.rb +380 -0
  163. data/test/tests.rb +25 -0
  164. data/test/tests_mimic.rb +14 -0
  165. data/test/tests_mimic_addition.rb +7 -0
  166. data/test/zoo.rb +13 -0
  167. metadata +381 -0
@@ -0,0 +1,433 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $oj_dir = File.dirname(File.expand_path(File.dirname(__FILE__)))
6
+ %w(lib ext).each do |dir|
7
+ $: << File.join($oj_dir, dir)
8
+ end
9
+
10
+ require 'minitest'
11
+ require 'minitest/autorun'
12
+ require 'stringio'
13
+ require 'date'
14
+ require 'bigdecimal'
15
+ require 'oj'
16
+
17
+ class StrictJuice < Minitest::Test
18
+
19
+ module TestModule
20
+ end
21
+
22
+ class Jeez
23
+ attr_accessor :x, :y
24
+
25
+ def initialize(x, y)
26
+ @x = x
27
+ @y = y
28
+ end
29
+ end
30
+
31
+ def setup
32
+ @default_options = Oj.default_options
33
+ # in strict mode other options other than the number formats are not used.
34
+ Oj.default_options = { :mode => :strict }
35
+ end
36
+
37
+ def teardown
38
+ Oj.default_options = @default_options
39
+ end
40
+
41
+ def test_nil
42
+ dump_and_load(nil, false)
43
+ end
44
+
45
+ def test_true
46
+ dump_and_load(true, false)
47
+ end
48
+
49
+ def test_false
50
+ dump_and_load(false, false)
51
+ end
52
+
53
+ def test_fixnum
54
+ dump_and_load(0, false)
55
+ dump_and_load(12345, false)
56
+ dump_and_load(-54321, false)
57
+ dump_and_load(1, false)
58
+ end
59
+
60
+ def test_float
61
+ dump_and_load(0.0, false)
62
+ dump_and_load(12345.6789, false)
63
+ dump_and_load(70.35, false)
64
+ dump_and_load(-54321.012, false)
65
+ dump_and_load(1.7775, false)
66
+ dump_and_load(2.5024, false)
67
+ dump_and_load(2.48e16, false)
68
+ dump_and_load(2.48e100 * 1.0e10, false)
69
+ dump_and_load(-2.48e100 * 1.0e10, false)
70
+ end
71
+
72
+ def test_nan_dump
73
+ assert_equal('null', Oj.dump(0/0.0, :nan => :null))
74
+ assert_equal('3.3e14159265358979323846', Oj.dump(0/0.0, :nan => :huge))
75
+ begin
76
+ Oj.dump(0/0.0, :nan => :word)
77
+ rescue Exception
78
+ assert(true)
79
+ return
80
+ end
81
+ assert(false, "*** expected an exception")
82
+ end
83
+
84
+ def test_infinity_dump
85
+ assert_equal('null', Oj.dump(1/0.0, :nan => :null))
86
+ assert_equal('3.0e14159265358979323846', Oj.dump(1/0.0, :nan => :huge))
87
+ begin
88
+ Oj.dump(1/0.0, :nan => :word)
89
+ rescue Exception
90
+ assert(true)
91
+ return
92
+ end
93
+ assert(false, "*** expected an exception")
94
+ end
95
+
96
+ def test_neg_infinity_dump
97
+ assert_equal('null', Oj.dump(-1/0.0, :nan => :null))
98
+ assert_equal('-3.0e14159265358979323846', Oj.dump(-1/0.0, :nan => :huge))
99
+ begin
100
+ Oj.dump(-1/0.0, :nan => :word)
101
+ rescue Exception
102
+ assert(true)
103
+ return
104
+ end
105
+ assert(false, "*** expected an exception")
106
+ end
107
+
108
+ def test_string
109
+ dump_and_load('', false)
110
+ dump_and_load('abc', false)
111
+ dump_and_load("abc\ndef", false)
112
+ dump_and_load("a\u0041", false)
113
+ end
114
+
115
+ def test_encode
116
+ opts = Oj.default_options
117
+ Oj.default_options = { :ascii_only => false }
118
+ unless 'jruby' == $ruby
119
+ dump_and_load("ぴーたー", false)
120
+ end
121
+ Oj.default_options = { :ascii_only => true }
122
+ json = Oj.dump("ぴーたー")
123
+ assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
124
+ unless 'jruby' == $ruby
125
+ dump_and_load("ぴーたー", false)
126
+ end
127
+ Oj.default_options = opts
128
+ end
129
+
130
+ def test_unicode
131
+ # hits the 3 normal ranges and one extended surrogate pair
132
+ json = %{"\\u019f\\u05e9\\u3074\\ud834\\udd1e"}
133
+ obj = Oj.load(json)
134
+ json2 = Oj.dump(obj, :ascii_only => true)
135
+ assert_equal(json, json2)
136
+ end
137
+
138
+ def test_unicode_long
139
+ # tests buffer overflow
140
+ json = %{"\\u019f\\u05e9\\u3074\\ud834\\udd1e #{'x' * 2000}"}
141
+ obj = Oj.load(json)
142
+ json2 = Oj.dump(obj, :ascii_only => true)
143
+ assert_equal(json, json2)
144
+ end
145
+
146
+ def test_array
147
+ dump_and_load([], false)
148
+ dump_and_load([true, false], false)
149
+ dump_and_load(['a', 1, nil], false)
150
+ dump_and_load([[nil]], false)
151
+ dump_and_load([[nil], 58], false)
152
+ end
153
+
154
+ def test_array_deep
155
+ dump_and_load([1,[2,[3,[4,[5,[6,[7,[8,[9,[10,[11,[12,[13,[14,[15,[16,[17,[18,[19,[20]]]]]]]]]]]]]]]]]]]], false)
156
+ end
157
+
158
+ def test_deep_nest
159
+ begin
160
+ n = 10000
161
+ Oj.strict_load('[' * n + ']' * n)
162
+ rescue Exception => e
163
+ assert(false, e.message)
164
+ end
165
+ end
166
+
167
+ # Hash
168
+ def test_hash
169
+ dump_and_load({}, false)
170
+ dump_and_load({ 'true' => true, 'false' => false}, false)
171
+ dump_and_load({ 'true' => true, 'array' => [], 'hash' => { }}, false)
172
+ end
173
+
174
+ def test_hash_deep
175
+ dump_and_load({'1' => {
176
+ '2' => {
177
+ '3' => {
178
+ '4' => {
179
+ '5' => {
180
+ '6' => {
181
+ '7' => {
182
+ '8' => {
183
+ '9' => {
184
+ '10' => {
185
+ '11' => {
186
+ '12' => {
187
+ '13' => {
188
+ '14' => {
189
+ '15' => {
190
+ '16' => {
191
+ '17' => {
192
+ '18' => {
193
+ '19' => {
194
+ '20' => {}}}}}}}}}}}}}}}}}}}}}, false)
195
+ end
196
+
197
+ def test_hash_escaped_key
198
+ json = %{{"a\nb":true,"c\td":false}}
199
+ obj = Oj.strict_load(json)
200
+ assert_equal({"a\nb" => true, "c\td" => false}, obj)
201
+ end
202
+
203
+ def test_non_str_hash
204
+ begin
205
+ Oj.dump({ 1 => true, 0 => false })
206
+ rescue Exception
207
+ assert(true)
208
+ return
209
+ end
210
+ assert(false, "*** expected an exception")
211
+ end
212
+
213
+ def test_bignum_object
214
+ dump_and_load(7 ** 55, false)
215
+ end
216
+
217
+ # BigDecimal
218
+ def test_bigdecimal_strict
219
+ Oj.default_options = { :bigdecimal_load => true}
220
+ dump_and_load(BigDecimal('3.14159265358979323846'), false)
221
+ end
222
+
223
+ def test_bigdecimal_load
224
+ orig = BigDecimal('80.51')
225
+ json = Oj.dump(orig, :mode => :strict, :bigdecimal_as_decimal => true)
226
+ bg = Oj.load(json, :mode => :strict, :bigdecimal_load => true)
227
+ assert_equal(BigDecimal, bg.class)
228
+ assert_equal(orig, bg)
229
+ end
230
+
231
+ def test_json_object
232
+ obj = Jeez.new(true, 58)
233
+ begin
234
+ Oj.dump(obj)
235
+ rescue Exception
236
+ assert(true)
237
+ return
238
+ end
239
+ assert(false, "*** expected an exception")
240
+ end
241
+
242
+ def test_range
243
+ begin
244
+ Oj.dump(1..7)
245
+ rescue Exception
246
+ assert(true)
247
+ return
248
+ end
249
+ assert(false, "*** expected an exception")
250
+ end
251
+
252
+ # Stream IO
253
+ def test_io_string
254
+ json = %{{
255
+ "x":true,
256
+ "y":58,
257
+ "z": [1,2,3]
258
+ }
259
+ }
260
+ input = StringIO.new(json)
261
+ obj = Oj.strict_load(input)
262
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
263
+ end
264
+
265
+ def test_io_file
266
+ filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
267
+ File.open(filename, 'w') { |f| f.write(%{{
268
+ "x":true,
269
+ "y":58,
270
+ "z": [1,2,3]
271
+ }
272
+ }) }
273
+ f = File.new(filename)
274
+ obj = Oj.strict_load(f)
275
+ f.close()
276
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
277
+ end
278
+
279
+ def test_symbol
280
+ json = Oj.dump(:abc)
281
+ assert_equal('"abc"', json)
282
+ end
283
+
284
+ def test_time
285
+ t = Time.local(2012, 1, 5, 23, 58, 7)
286
+ begin
287
+ Oj.dump(t)
288
+ rescue Exception
289
+ assert(true)
290
+ return
291
+ end
292
+ assert(false, "*** expected an exception")
293
+ end
294
+
295
+ def test_class
296
+ begin
297
+ Oj.dump(StrictJuice)
298
+ rescue Exception
299
+ assert(true)
300
+ return
301
+ end
302
+ assert(false, "*** expected an exception")
303
+ end
304
+
305
+ def test_module
306
+ begin
307
+ Oj.dump(TestModule)
308
+ rescue Exception
309
+ assert(true)
310
+ return
311
+ end
312
+ assert(false, "*** expected an exception")
313
+ end
314
+
315
+ # symbol_keys option
316
+ def test_symbol_keys
317
+ json = %{{
318
+ "x":true,
319
+ "y":58,
320
+ "z": [1,2,3]
321
+ }
322
+ }
323
+ obj = Oj.strict_load(json, :symbol_keys => true)
324
+ assert_equal({ :x => true, :y => 58, :z => [1, 2, 3]}, obj)
325
+ end
326
+
327
+ def test_symbol_keys_safe
328
+ json = %{{
329
+ "x":true,
330
+ "y":58,
331
+ "z": [1,2,3]
332
+ }
333
+ }
334
+ obj = Oj.safe_load(json)
335
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
336
+ end
337
+
338
+ # comments
339
+ def test_comment_slash
340
+ json = %{{
341
+ "x":true,//three
342
+ "y":58,
343
+ "z": [1,2,
344
+ 3 // six
345
+ ]}
346
+ }
347
+ obj = Oj.strict_load(json)
348
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
349
+ end
350
+
351
+ def test_comment_c
352
+ json = %{{
353
+ "x"/*one*/:/*two*/true,
354
+ "y":58,
355
+ "z": [1,2,3]}
356
+ }
357
+ obj = Oj.strict_load(json)
358
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
359
+ end
360
+
361
+ def test_comment
362
+ json = %{{
363
+ "x"/*one*/:/*two*/true,//three
364
+ "y":58/*four*/,
365
+ "z": [1,2/*five*/,
366
+ 3 // six
367
+ ]
368
+ }
369
+ }
370
+ obj = Oj.strict_load(json)
371
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
372
+ end
373
+
374
+ def test_double
375
+ json = %{{ "x": 1}{ "y": 2}}
376
+ results = []
377
+ Oj.load(json, :mode => :strict) { |x| results << x }
378
+
379
+ assert_equal([{ 'x' => 1 }, { 'y' => 2 }], results)
380
+ end
381
+
382
+ def test_invalid_decimal_dot_start
383
+ assert_raises(Oj::ParseError) {
384
+ Oj.load('.123', mode: :strict)
385
+ }
386
+ assert_raises(Oj::ParseError) {
387
+ Oj.load('-.123', mode: :strict)
388
+ }
389
+ end
390
+
391
+ def test_invalid_decimal_dot_end
392
+ json = '123.'
393
+ assert_raises(Oj::ParseError) {
394
+ Oj.load(json, mode: :strict)
395
+ }
396
+ end
397
+
398
+ def test_invalid_decimal_plus
399
+ json = '+12'
400
+ assert_raises(Oj::ParseError) {
401
+ Oj.load(json, mode: :strict)
402
+ }
403
+ end
404
+
405
+ def test_circular_hash
406
+ h = { 'a' => 7 }
407
+ h['b'] = h
408
+ json = Oj.dump(h, :indent => 2, :circular => true)
409
+ assert_equal(%|{
410
+ "a":7,
411
+ "b":null
412
+ }
413
+ |, json)
414
+ end
415
+
416
+ def test_omit_nil
417
+ json = Oj.dump({'x' => {'a' => 1, 'b' => nil }, 'y' => nil}, :omit_nil => true)
418
+ assert_equal(%|{"x":{"a":1}}|, json)
419
+ end
420
+
421
+ def dump_and_load(obj, trace=false)
422
+ json = Oj.dump(obj, :indent => 2)
423
+ puts json if trace
424
+ loaded = Oj.strict_load(json);
425
+ if obj.nil?
426
+ assert_nil(loaded)
427
+ else
428
+ assert_equal(obj, loaded)
429
+ end
430
+ loaded
431
+ end
432
+
433
+ end
@@ -0,0 +1,719 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+
6
+ require 'helper'
7
+
8
+ class Juice < Minitest::Test
9
+ def gen_whitespaced_string(length = Random.new.rand(100))
10
+ whitespace_chars = [" ", "\t", "\f", "\n", "\r"]
11
+ result = ""
12
+ length.times { result << whitespace_chars.sample }
13
+ result
14
+ end
15
+
16
+ module TestModule
17
+ end
18
+
19
+ class Jam
20
+ attr_accessor :x, :y
21
+
22
+ def initialize(x, y)
23
+ @x = x
24
+ @y = y
25
+ end
26
+
27
+ def eql?(o)
28
+ self.class == o.class && @x == o.x && @y == o.y
29
+ end
30
+ alias == eql?
31
+
32
+ end # Jam
33
+
34
+ class Jeez < Jam
35
+ def initialize(x, y)
36
+ super
37
+ end
38
+
39
+ def to_json()
40
+ %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
41
+ end
42
+
43
+ def self.json_create(h)
44
+ self.new(h['x'], h['y'])
45
+ end
46
+ end # Jeez
47
+
48
+ # contributed by sauliusg to fix as_json
49
+ class Orange < Jam
50
+ def initialize(x, y)
51
+ super
52
+ end
53
+
54
+ def as_json()
55
+ { :json_class => self.class,
56
+ :x => @x,
57
+ :y => @y }
58
+ end
59
+
60
+ def self.json_create(h)
61
+ self.new(h['x'], h['y'])
62
+ end
63
+ end
64
+
65
+ class Melon < Jam
66
+ def initialize(x, y)
67
+ super
68
+ end
69
+
70
+ def as_json(options)
71
+ "#{x} #{y}"
72
+ end
73
+
74
+ def self.json_create(h)
75
+ self.new(h['x'], h['y'])
76
+ end
77
+ end
78
+
79
+ class Jazz < Jam
80
+ def initialize(x, y)
81
+ super
82
+ end
83
+ def to_hash()
84
+ { 'json_class' => self.class.to_s, 'x' => @x, 'y' => @y }
85
+ end
86
+ def self.json_create(h)
87
+ self.new(h['x'], h['y'])
88
+ end
89
+ end # Jazz
90
+
91
+ def setup
92
+ @default_options = Oj.default_options
93
+ end
94
+
95
+ def teardown
96
+ Oj.default_options = @default_options
97
+ end
98
+
99
+ def test_set_options
100
+ orig = Oj.default_options()
101
+ alt ={
102
+ indent: " - ",
103
+ second_precision: 5,
104
+ circular: true,
105
+ class_cache: false,
106
+ auto_define: true,
107
+ symbol_keys: true,
108
+ bigdecimal_as_decimal: false,
109
+ use_to_json: false,
110
+ use_to_hash: false,
111
+ use_as_json: false,
112
+ use_raw_json: false,
113
+ nilnil: true,
114
+ empty_string: true,
115
+ allow_gc: false,
116
+ quirks_mode: false,
117
+ allow_invalid_unicode: true,
118
+ float_precision: 13,
119
+ mode: :strict,
120
+ escape_mode: :ascii,
121
+ time_format: :unix_zone,
122
+ bigdecimal_load: :float,
123
+ create_id: 'classy',
124
+ create_additions: true,
125
+ space: 'z',
126
+ array_nl: 'a',
127
+ object_nl: 'o',
128
+ space_before: 'b',
129
+ nan: :huge,
130
+ hash_class: Hash,
131
+ omit_nil: false,
132
+ allow_nan: true,
133
+ integer_range: nil,
134
+ array_class: Array,
135
+ ignore: nil,
136
+ ignore_under: true,
137
+ trace: true,
138
+ safe: true,
139
+ }
140
+ Oj.default_options = alt
141
+ #keys = alt.keys
142
+ #Oj.default_options.keys.each { |k| puts k unless keys.include? k}
143
+ opts = Oj.default_options()
144
+ assert_equal(alt, opts);
145
+
146
+ Oj.default_options = orig # return to original
147
+ # verify back to original
148
+ opts = Oj.default_options()
149
+ assert_equal(orig, opts);
150
+ end
151
+
152
+ def test_nil
153
+ dump_and_load(nil, false)
154
+ end
155
+
156
+ def test_true
157
+ dump_and_load(true, false)
158
+ end
159
+
160
+ def test_false
161
+ dump_and_load(false, false)
162
+ end
163
+
164
+ def test_fixnum
165
+ dump_and_load(0, false)
166
+ dump_and_load(12345, false)
167
+ dump_and_load(-54321, false)
168
+ dump_and_load(1, false)
169
+ end
170
+
171
+ def test_float_parse
172
+ Oj.default_options = { :float_precision => 16, :bigdecimal_load => :auto }
173
+ n = Oj.load('0.00001234567890123456')
174
+ assert_equal(Float, n.class)
175
+ assert_equal('1.234567890123456e-05', "%0.15e" % [n])
176
+
177
+ n = Oj.load('-0.00001234567890123456')
178
+ assert_equal(Float, n.class)
179
+ assert_equal('-1.234567890123456e-05', "%0.15e" % [n])
180
+
181
+ n = Oj.load('1000.0000123456789')
182
+ assert_equal(BigDecimal, n.class)
183
+ assert_equal('0.10000000123456789E4', n.to_s.upcase)
184
+
185
+ n = Oj.load('-0.000012345678901234567')
186
+ assert_equal(BigDecimal, n.class)
187
+ assert_equal('-0.12345678901234567E-4', n.to_s.upcase)
188
+ end
189
+
190
+ =begin
191
+ # TBD move to custom
192
+ def test_float_dump
193
+ Oj.default_options = { :float_precision => 16 }
194
+ assert_equal('1405460727.723866', Oj.dump(1405460727.723866))
195
+ Oj.default_options = { :float_precision => 5 }
196
+ assert_equal('1.4055', Oj.dump(1.405460727))
197
+ Oj.default_options = { :float_precision => 0 }
198
+ assert_equal('1405460727.723866', Oj.dump(1405460727.723866))
199
+ Oj.default_options = { :float_precision => 15 }
200
+ assert_equal('0.56', Oj.dump(0.56))
201
+ assert_equal('0.5773', Oj.dump(0.5773))
202
+ assert_equal('0.6768', Oj.dump(0.6768))
203
+ assert_equal('0.685', Oj.dump(0.685))
204
+ assert_equal('0.7032', Oj.dump(0.7032))
205
+ assert_equal('0.7051', Oj.dump(0.7051))
206
+ assert_equal('0.8274', Oj.dump(0.8274))
207
+ assert_equal('0.9149', Oj.dump(0.9149))
208
+ assert_equal('64.4', Oj.dump(64.4))
209
+ assert_equal('71.6', Oj.dump(71.6))
210
+ assert_equal('73.4', Oj.dump(73.4))
211
+ assert_equal('80.6', Oj.dump(80.6))
212
+ assert_equal('-95.640172', Oj.dump(-95.640172))
213
+ end
214
+ =end
215
+
216
+ def test_string
217
+ dump_and_load('', false)
218
+ dump_and_load('abc', false)
219
+ dump_and_load("abc\ndef", false)
220
+ dump_and_load("a\u0041", false)
221
+ assert_equal("a\u0000a", dump_and_load("a\u0000a", false))
222
+ end
223
+
224
+ def test_encode
225
+ opts = Oj.default_options
226
+ Oj.default_options = { :ascii_only => false }
227
+ dump_and_load("ぴーたー", false)
228
+
229
+ Oj.default_options = { :ascii_only => true }
230
+ json = Oj.dump("ぴーたー")
231
+ assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
232
+ dump_and_load("ぴーたー", false)
233
+ Oj.default_options = opts
234
+ end
235
+
236
+ def test_unicode
237
+ # hits the 3 normal ranges and one extended surrogate pair
238
+ json = %{"\\u019f\\u05e9\\u3074\\ud834\\udd1e"}
239
+ obj = Oj.load(json)
240
+ json2 = Oj.dump(obj, :ascii_only => true)
241
+ assert_equal(json, json2)
242
+ end
243
+
244
+ def test_invalid_unicode_raise
245
+ # validate that an invalid unicode raises unless the :allow_invalid_unicode is true
246
+ json = %{"x\\ud83dy"}
247
+ begin
248
+ Oj.load(json)
249
+ rescue Exception
250
+ assert(true)
251
+ return
252
+ end
253
+ assert(false, "*** expected an exception")
254
+ end
255
+
256
+ def test_invalid_unicode_ok
257
+ # validate that an invalid unicode raises unless the :allow_invalid_unicode is true
258
+ json = %{"x\\ud83dy"}
259
+ obj = Oj.load(json, :allow_invalid_unicode => true)
260
+ # The same as what ruby would do with the invalid encoding.
261
+ assert_equal("x\xED\xA0\xBDy", obj.to_s)
262
+ end
263
+
264
+ def test_dump_options
265
+ json = Oj.dump({ 'a' => 1, 'b' => [true, false]},
266
+ :mode => :compat,
267
+ :indent => "--",
268
+ :array_nl => "\n",
269
+ :object_nl => "#\n",
270
+ :space => "*",
271
+ :space_before => "~")
272
+ assert(%{{#
273
+ --"a"~:*1,#
274
+ --"b"~:*[
275
+ ----true,
276
+ ----false
277
+ --]#
278
+ }} == json ||
279
+ %{{#
280
+ --"b"~:*[
281
+ ----true,
282
+ ----false
283
+ --],#
284
+ --"a"~:*1#
285
+ }} == json)
286
+
287
+ end
288
+
289
+ def test_null_char
290
+ assert_raises(Oj::ParseError) { Oj.load("\"\0\"") }
291
+ assert_raises(Oj::ParseError) { Oj.load("\"\\\0\"") }
292
+ end
293
+
294
+ def test_array
295
+ dump_and_load([], false)
296
+ dump_and_load([true, false], false)
297
+ dump_and_load(['a', 1, nil], false)
298
+ dump_and_load([[nil]], false)
299
+ dump_and_load([[nil], 58], false)
300
+ end
301
+ def test_array_not_closed
302
+ begin
303
+ Oj.load('[')
304
+ rescue Exception
305
+ assert(true)
306
+ return
307
+ end
308
+ assert(false, "*** expected an exception")
309
+ end
310
+
311
+ # multiple JSON in one string
312
+ def test_multiple_json_callback
313
+ json = %{{"a":1}
314
+ [1,2][3,4]
315
+ {"b":2}
316
+ }
317
+ results = []
318
+ Oj.load(json, :mode => :strict) { |x, start, len| results << [x, start, len] }
319
+ assert_equal([[{"a"=>1}, 0, 7], [[1,2], 7, 6], [[3,4], 13, 5], [{"b"=>2}, 18, 8]], results)
320
+ end
321
+
322
+ def test_multiple_json_no_callback
323
+ json = %{{"a":1}
324
+ [1,2][3,4]
325
+ {"b":2}
326
+ }
327
+ assert_raises(Oj::ParseError) { Oj.load(json) }
328
+ end
329
+
330
+ # encoding tests
331
+ def test_does_not_escape_entities_by_default
332
+ Oj.default_options = { :escape_mode => :ascii } # set in mimic mode
333
+ # use Oj to create the hash since some Rubies don't deal nicely with unicode.
334
+ json = %{{"key":"I <3 this\\u2028space"}}
335
+ hash = Oj.load(json)
336
+ out = Oj.dump(hash)
337
+ assert_equal(json, out)
338
+ end
339
+ def test_escapes_entities_by_default_when_configured_to_do_so
340
+ hash = {'key' => "I <3 this"}
341
+ Oj.default_options = {:escape_mode => :xss_safe}
342
+ out = Oj.dump hash
343
+ assert_equal(%{{"key":"I \\u003c3 this"}}, out)
344
+ end
345
+ def test_escapes_entities_when_asked_to
346
+ hash = {'key' => "I <3 this"}
347
+ out = Oj.dump(hash, :escape_mode => :xss_safe)
348
+ assert_equal(%{{"key":"I \\u003c3 this"}}, out)
349
+ end
350
+ def test_does_not_escape_entities_when_not_asked_to
351
+ hash = {'key' => "I <3 this"}
352
+ out = Oj.dump(hash, :escape_mode => :json)
353
+ assert_equal(%{{"key":"I <3 this"}}, out)
354
+ end
355
+ def test_escapes_common_xss_vectors
356
+ hash = {'key' => "<script>alert(123) && formatHD()</script>"}
357
+ out = Oj.dump(hash, :escape_mode => :xss_safe)
358
+ assert_equal(%{{"key":"\\u003cscript\\u003ealert(123) \\u0026\\u0026 formatHD()\\u003c\\/script\\u003e"}}, out)
359
+ end
360
+ def test_escape_newline_by_default
361
+ Oj.default_options = { :escape_mode => :json }
362
+ json = %{["one","two\\n2"]}
363
+ x = Oj.load(json)
364
+ out = Oj.dump(x)
365
+ assert_equal(json, out)
366
+ end
367
+ def test_does_not_escape_newline
368
+ Oj.default_options = { :escape_mode => :newline }
369
+ json = %{["one","two\n2"]}
370
+ x = Oj.load(json)
371
+ out = Oj.dump(x)
372
+ assert_equal(json, out)
373
+ end
374
+ def test_dump_invalid_utf8
375
+ Oj.default_options = { :escape_mode => :ascii }
376
+ assert_raises(EncodingError) {
377
+ Oj.dump(["abc\xbe\x1f\x11"], mode: :strict)
378
+ }
379
+ end
380
+
381
+ # Symbol
382
+ def test_symbol_null
383
+ json = Oj.dump(:abc, :mode => :null)
384
+ assert_equal('"abc"', json)
385
+ end
386
+
387
+ # Time
388
+ def test_time_null
389
+ t = Time.local(2012, 1, 5, 23, 58, 7)
390
+ json = Oj.dump(t, :mode => :null)
391
+ assert_equal('null', json)
392
+ end
393
+
394
+ def test_time_neg
395
+ t = Time.parse("1900-01-01 00:18:59 UTC")
396
+ json = Oj.dump(t, :mode => :custom, :time_format => :unix)
397
+ assert_equal('-2208987661.000000000', json)
398
+ end
399
+
400
+ def test_time_years
401
+ (-2020..2020).each { |year|
402
+ s = "%04d-03-01T15:14:13Z" % [year]
403
+ json = Oj.dump(Time.parse(s), mode: :custom, time_format: :xmlschema, second_precision: -1)
404
+ assert_equal(s, json[1..-2])
405
+
406
+ json = Oj.dump(Time.parse(s), mode: :custom, time_format: :xmlschema, second_precision: 3)
407
+ assert_equal(s[0..-2] + '.000Z', json[1..-2])
408
+ }
409
+ end
410
+
411
+ # Class
412
+ def test_class_null
413
+ json = Oj.dump(Juice, :mode => :null)
414
+ assert_equal('null', json)
415
+ end
416
+
417
+ # Module
418
+ def test_module_null
419
+ json = Oj.dump(TestModule, :mode => :null)
420
+ assert_equal('null', json)
421
+ end
422
+
423
+ # Hash
424
+ def test_non_str_hash_null
425
+ begin
426
+ Oj.dump({ 1 => true, 0 => false }, :mode => :null)
427
+ rescue Exception
428
+ assert(true)
429
+ return
430
+ end
431
+ assert(false, "*** expected an exception")
432
+ end
433
+
434
+ def test_hash_not_closed
435
+ begin
436
+ Oj.load('{')
437
+ rescue Exception
438
+ assert(true)
439
+ return
440
+ end
441
+ assert(false, "*** expected an exception")
442
+ end
443
+
444
+ # Object with to_json()
445
+ def test_json_object_null
446
+ obj = Jeez.new(true, 58)
447
+ json = Oj.dump(obj, :mode => :null)
448
+ assert_equal('null', json)
449
+ end
450
+
451
+ # Object with to_hash()
452
+ def test_to_hash_object_null
453
+ obj = Jazz.new(true, 58)
454
+ json = Oj.dump(obj, :mode => :null)
455
+ assert_equal('null', json)
456
+ end
457
+
458
+ # Object with as_json() # contributed by sauliusg
459
+ def test_as_json_object_null
460
+ obj = Orange.new(true, 58)
461
+ json = Oj.dump(obj, :mode => :null)
462
+ assert_equal('null', json)
463
+ end
464
+
465
+ # Object without to_json() or to_hash()
466
+ def test_object_null
467
+ obj = Jam.new(true, 58)
468
+ json = Oj.dump(obj, :mode => :null)
469
+ assert_equal('null', json)
470
+ end
471
+
472
+ # Range
473
+ def test_range_null
474
+ json = Oj.dump(1..7, :mode => :null)
475
+ assert_equal('null', json)
476
+ end
477
+
478
+ # BigNum
479
+ def test_bignum_null
480
+ json = Oj.dump(7 ** 55, :mode => :null)
481
+ assert_equal('30226801971775055948247051683954096612865741943', json)
482
+ end
483
+
484
+ def test_bignum_object
485
+ dump_and_load(7 ** 55, false)
486
+ dump_and_load(10 ** 19, false)
487
+ end
488
+
489
+ # BigDecimal
490
+ def test_bigdecimal_null
491
+ mode = Oj.default_options[:mode]
492
+ Oj.default_options = {:mode => :null}
493
+ dump_and_load(BigDecimal('3.14159265358979323846'), false)
494
+ Oj.default_options = {:mode => mode}
495
+ end
496
+
497
+ def test_infinity
498
+ n = Oj.load('Infinity', :mode => :object)
499
+ assert_equal(BigDecimal('Infinity').to_f, n);
500
+ x = Oj.load('Infinity', :mode => :compat)
501
+ assert_equal('Infinity', x.to_s)
502
+ end
503
+
504
+ # Date
505
+ def test_date_null
506
+ json = Oj.dump(Date.new(2012, 6, 19), :mode => :null)
507
+ assert_equal('null', json)
508
+ end
509
+
510
+ # DateTime
511
+ def test_datetime_null
512
+ json = Oj.dump(DateTime.new(2012, 6, 19, 20, 19, 27), :mode => :null)
513
+ assert_equal('null', json)
514
+ end
515
+
516
+ # autodefine Oj::Bag
517
+ def test_bag
518
+ json = %{{
519
+ "^o":"Juice::Jem",
520
+ "x":true,
521
+ "y":58 }}
522
+ obj = Oj.load(json, :mode => :object, :auto_define => true)
523
+ assert_equal('Juice::Jem', obj.class.name)
524
+ assert_equal(true, obj.x)
525
+ assert_equal(58, obj.y)
526
+ end
527
+
528
+ # Stream Deeply Nested
529
+ def test_deep_nest_dump
530
+ begin
531
+ a = []
532
+ 10000.times { a << [a] }
533
+ Oj.dump(a)
534
+ rescue Exception
535
+ assert(true)
536
+ return
537
+ end
538
+ assert(false, "*** expected an exception")
539
+ end
540
+
541
+ # Stream IO
542
+ def test_io_string
543
+ src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
544
+ output = StringIO.open("", "w+")
545
+ Oj.to_stream(output, src)
546
+
547
+ input = StringIO.new(output.string())
548
+ obj = Oj.load(input, :mode => :strict)
549
+ assert_equal(src, obj)
550
+ end
551
+
552
+ def test_io_file
553
+ src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
554
+ filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
555
+ File.open(filename, "w") { |f|
556
+ Oj.to_stream(f, src)
557
+ }
558
+ f = File.new(filename)
559
+ obj = Oj.load(f, :mode => :strict)
560
+ f.close()
561
+ assert_equal(src, obj)
562
+ end
563
+
564
+ # comments
565
+ def test_comment_slash
566
+ json = %{{
567
+ "x":true,//three
568
+ "y":58,
569
+ "z": [1,2,
570
+ 3 // six
571
+ ]}
572
+ }
573
+ obj = Oj.load(json, :mode => :strict)
574
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
575
+ end
576
+
577
+ def test_comment_c
578
+ json = %{/*before*/
579
+ {
580
+ "x"/*one*/:/*two*/true,
581
+ "y":58,
582
+ "z": [1,2,3]}
583
+ }
584
+ obj = Oj.load(json, :mode => :strict)
585
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
586
+ end
587
+
588
+ def test_comment
589
+ json = %{{
590
+ "x"/*one*/:/*two*/true,//three
591
+ "y":58/*four*/,
592
+ "z": [1,2/*five*/,
593
+ 3 // six
594
+ ]
595
+ }
596
+ }
597
+ obj = Oj.load(json, :mode => :strict)
598
+ assert_equal({ 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}, obj)
599
+ end
600
+
601
+ def test_nilnil_false
602
+ begin
603
+ Oj.load(nil, :nilnil => false)
604
+ rescue Exception
605
+ assert(true)
606
+ return
607
+ end
608
+ assert(false, "*** expected an exception")
609
+ end
610
+
611
+ def test_nilnil_true
612
+ obj = Oj.load(nil, :nilnil => true)
613
+ assert_nil(obj)
614
+ end
615
+
616
+ def test_empty_string_true
617
+ result = Oj.load(gen_whitespaced_string, :empty_string => true, mode: :strict)
618
+ assert_nil(result)
619
+ end
620
+
621
+ def test_empty_string_false
622
+ # Could be either a Oj::ParseError or an EncodingError depending on
623
+ # whether mimic_JSON has been called. Since we don't know when the test
624
+ # will be called either is okay.
625
+ begin
626
+ Oj.load(gen_whitespaced_string, :empty_string => false)
627
+ rescue Exception => e
628
+ assert(Oj::ParseError == e.class || EncodingError == e.class)
629
+ return
630
+ end
631
+ assert(false, "*** expected an exception")
632
+ end
633
+
634
+ def test_quirks_null_mode
635
+ assert_raises(Oj::ParseError) { Oj.load("null", :quirks_mode => false) }
636
+ assert_nil(Oj.load("null", :quirks_mode => true))
637
+ end
638
+
639
+ def test_quirks_bool_mode
640
+ assert_raises(Oj::ParseError) { Oj.load("true", :quirks_mode => false) }
641
+ assert_equal(true, Oj.load("true", :quirks_mode => true))
642
+ end
643
+
644
+ def test_quirks_number_mode
645
+ assert_raises(Oj::ParseError) { Oj.load("123", :quirks_mode => false) }
646
+ assert_equal(123, Oj.load("123", :quirks_mode => true))
647
+ end
648
+
649
+ def test_quirks_decimal_mode
650
+ assert_raises(Oj::ParseError) { Oj.load("123.45", :quirks_mode => false) }
651
+ assert_equal(123.45, Oj.load("123.45", :quirks_mode => true))
652
+ end
653
+
654
+ def test_quirks_string_mode
655
+ assert_raises(Oj::ParseError) { Oj.load('"string"', :quirks_mode => false) }
656
+ assert_equal('string', Oj.load('"string"', :quirks_mode => true))
657
+ end
658
+
659
+ def test_error_path
660
+ msg = ''
661
+ assert_raises(Oj::ParseError) {
662
+ begin
663
+ Oj.load(%|{
664
+ "first": [
665
+ 1, 2, { "third": 123x }
666
+ ]
667
+ }|)
668
+ rescue Oj::ParseError => e
669
+ msg = e.message
670
+ raise e
671
+ end
672
+ }
673
+ assert_equal('after first[2].third', msg.split('(')[1].split(')')[0])
674
+ end
675
+
676
+ def test_bad_bignum
677
+ if '2.4.0' < RUBY_VERSION
678
+ assert_raises Oj::ParseError do
679
+ Oj.load(%|{ "big": -e123456789 }|, mode: :strict)
680
+ end
681
+ end
682
+ end
683
+
684
+ def test_quirks_array_mode
685
+ assert_equal([], Oj.load("[]", :quirks_mode => false))
686
+ assert_equal([], Oj.load("[]", :quirks_mode => true))
687
+ end
688
+
689
+ def test_quirks_object_mode
690
+ assert_equal({}, Oj.load("{}", :quirks_mode => false))
691
+ assert_equal({}, Oj.load("{}", :quirks_mode => true))
692
+ end
693
+
694
+ def test_omit_nil
695
+ jam = Jam.new({'a' => 1, 'b' => nil }, nil)
696
+
697
+ json = Oj.dump(jam, :omit_nil => true, :mode => :object)
698
+ assert_equal(%|{"^o":"Juice::Jam","x":{"a":1}}|, json)
699
+
700
+ json = Oj.dump({'x' => {'a' => 1, 'b' => nil }, 'y' => nil}, :omit_nil => true, :mode => :strict)
701
+ assert_equal(%|{"x":{"a":1}}|, json)
702
+
703
+ json = Oj.dump({'x' => {'a' => 1, 'b' => nil }, 'y' => nil}, :omit_nil => true, :mode => :null)
704
+ assert_equal(%|{"x":{"a":1}}|, json)
705
+ end
706
+
707
+ def dump_and_load(obj, trace=false)
708
+ json = Oj.dump(obj, :indent => 2)
709
+ puts json if trace
710
+ loaded = Oj.load(json)
711
+ if obj.nil?
712
+ assert_nil(loaded)
713
+ else
714
+ assert_equal(obj, loaded)
715
+ end
716
+ loaded
717
+ end
718
+
719
+ end