oj 3.11.5 → 3.16.5

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 (168) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1421 -0
  3. data/README.md +19 -5
  4. data/RELEASE_NOTES.md +61 -0
  5. data/ext/oj/buf.h +20 -6
  6. data/ext/oj/cache.c +329 -0
  7. data/ext/oj/cache.h +22 -0
  8. data/ext/oj/cache8.c +10 -9
  9. data/ext/oj/circarray.c +8 -6
  10. data/ext/oj/circarray.h +2 -2
  11. data/ext/oj/code.c +19 -33
  12. data/ext/oj/code.h +2 -2
  13. data/ext/oj/compat.c +27 -77
  14. data/ext/oj/custom.c +86 -179
  15. data/ext/oj/debug.c +126 -0
  16. data/ext/oj/dump.c +256 -249
  17. data/ext/oj/dump.h +26 -12
  18. data/ext/oj/dump_compat.c +565 -642
  19. data/ext/oj/dump_leaf.c +17 -63
  20. data/ext/oj/dump_object.c +65 -187
  21. data/ext/oj/dump_strict.c +27 -51
  22. data/ext/oj/encoder.c +43 -0
  23. data/ext/oj/err.c +2 -13
  24. data/ext/oj/err.h +24 -8
  25. data/ext/oj/extconf.rb +21 -6
  26. data/ext/oj/fast.c +149 -149
  27. data/ext/oj/intern.c +313 -0
  28. data/ext/oj/intern.h +22 -0
  29. data/ext/oj/mem.c +318 -0
  30. data/ext/oj/mem.h +53 -0
  31. data/ext/oj/mimic_json.c +121 -106
  32. data/ext/oj/object.c +85 -162
  33. data/ext/oj/odd.c +89 -67
  34. data/ext/oj/odd.h +15 -15
  35. data/ext/oj/oj.c +542 -411
  36. data/ext/oj/oj.h +99 -73
  37. data/ext/oj/parse.c +175 -187
  38. data/ext/oj/parse.h +26 -24
  39. data/ext/oj/parser.c +1600 -0
  40. data/ext/oj/parser.h +101 -0
  41. data/ext/oj/rails.c +112 -159
  42. data/ext/oj/rails.h +1 -1
  43. data/ext/oj/reader.c +11 -14
  44. data/ext/oj/reader.h +4 -2
  45. data/ext/oj/resolve.c +5 -24
  46. data/ext/oj/rxclass.c +7 -6
  47. data/ext/oj/rxclass.h +1 -1
  48. data/ext/oj/saj.c +22 -33
  49. data/ext/oj/saj2.c +584 -0
  50. data/ext/oj/saj2.h +23 -0
  51. data/ext/oj/scp.c +5 -28
  52. data/ext/oj/sparse.c +28 -72
  53. data/ext/oj/stream_writer.c +50 -40
  54. data/ext/oj/strict.c +56 -61
  55. data/ext/oj/string_writer.c +72 -39
  56. data/ext/oj/trace.h +31 -4
  57. data/ext/oj/usual.c +1218 -0
  58. data/ext/oj/usual.h +69 -0
  59. data/ext/oj/util.h +1 -1
  60. data/ext/oj/val_stack.c +14 -3
  61. data/ext/oj/val_stack.h +8 -7
  62. data/ext/oj/validate.c +46 -0
  63. data/ext/oj/wab.c +63 -88
  64. data/lib/oj/active_support_helper.rb +1 -3
  65. data/lib/oj/bag.rb +7 -1
  66. data/lib/oj/easy_hash.rb +4 -5
  67. data/lib/oj/error.rb +1 -2
  68. data/lib/oj/json.rb +162 -150
  69. data/lib/oj/mimic.rb +9 -7
  70. data/lib/oj/saj.rb +20 -6
  71. data/lib/oj/schandler.rb +5 -4
  72. data/lib/oj/state.rb +12 -8
  73. data/lib/oj/version.rb +1 -2
  74. data/lib/oj.rb +2 -0
  75. data/pages/Compatibility.md +1 -1
  76. data/pages/InstallOptions.md +20 -0
  77. data/pages/JsonGem.md +15 -0
  78. data/pages/Modes.md +8 -3
  79. data/pages/Options.md +43 -5
  80. data/pages/Parser.md +309 -0
  81. data/pages/Rails.md +14 -2
  82. data/test/_test_active.rb +8 -9
  83. data/test/_test_active_mimic.rb +7 -8
  84. data/test/_test_mimic_rails.rb +17 -20
  85. data/test/activerecord/result_test.rb +5 -6
  86. data/test/activesupport6/encoding_test.rb +63 -28
  87. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  88. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  89. data/test/{activesupport5 → activesupport7}/encoding_test.rb +86 -50
  90. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  91. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  92. data/test/files.rb +15 -15
  93. data/test/foo.rb +16 -45
  94. data/test/helper.rb +11 -8
  95. data/test/isolated/shared.rb +3 -2
  96. data/test/json_gem/json_addition_test.rb +2 -2
  97. data/test/json_gem/json_common_interface_test.rb +8 -6
  98. data/test/json_gem/json_encoding_test.rb +0 -0
  99. data/test/json_gem/json_ext_parser_test.rb +1 -0
  100. data/test/json_gem/json_fixtures_test.rb +3 -2
  101. data/test/json_gem/json_generator_test.rb +56 -38
  102. data/test/json_gem/json_generic_object_test.rb +11 -11
  103. data/test/json_gem/json_parser_test.rb +54 -47
  104. data/test/json_gem/json_string_matching_test.rb +9 -9
  105. data/test/json_gem/test_helper.rb +7 -3
  106. data/test/mem.rb +34 -0
  107. data/test/perf.rb +22 -27
  108. data/test/perf_compat.rb +31 -33
  109. data/test/perf_dump.rb +50 -0
  110. data/test/perf_fast.rb +80 -82
  111. data/test/perf_file.rb +27 -29
  112. data/test/perf_object.rb +65 -69
  113. data/test/perf_once.rb +59 -0
  114. data/test/perf_parser.rb +183 -0
  115. data/test/perf_saj.rb +46 -54
  116. data/test/perf_scp.rb +58 -69
  117. data/test/perf_simple.rb +41 -39
  118. data/test/perf_strict.rb +74 -82
  119. data/test/perf_wab.rb +67 -69
  120. data/test/prec.rb +5 -5
  121. data/test/sample/change.rb +0 -1
  122. data/test/sample/dir.rb +0 -1
  123. data/test/sample/doc.rb +0 -1
  124. data/test/sample/file.rb +0 -1
  125. data/test/sample/group.rb +0 -1
  126. data/test/sample/hasprops.rb +0 -1
  127. data/test/sample/layer.rb +0 -1
  128. data/test/sample/rect.rb +0 -1
  129. data/test/sample/shape.rb +0 -1
  130. data/test/sample/text.rb +0 -1
  131. data/test/sample.rb +16 -16
  132. data/test/sample_json.rb +8 -8
  133. data/test/test_compat.rb +95 -43
  134. data/test/test_custom.rb +73 -51
  135. data/test/test_debian.rb +7 -10
  136. data/test/test_fast.rb +135 -79
  137. data/test/test_file.rb +41 -30
  138. data/test/test_gc.rb +16 -5
  139. data/test/test_generate.rb +5 -5
  140. data/test/test_hash.rb +5 -5
  141. data/test/test_integer_range.rb +9 -9
  142. data/test/test_null.rb +20 -20
  143. data/test/test_object.rb +99 -96
  144. data/test/test_parser.rb +11 -0
  145. data/test/test_parser_debug.rb +27 -0
  146. data/test/test_parser_saj.rb +337 -0
  147. data/test/test_parser_usual.rb +251 -0
  148. data/test/test_rails.rb +2 -2
  149. data/test/test_saj.rb +10 -8
  150. data/test/test_scp.rb +37 -39
  151. data/test/test_strict.rb +40 -32
  152. data/test/test_various.rb +165 -84
  153. data/test/test_wab.rb +48 -44
  154. data/test/test_writer.rb +47 -47
  155. data/test/tests.rb +13 -5
  156. data/test/tests_mimic.rb +12 -3
  157. data/test/tests_mimic_addition.rb +12 -3
  158. metadata +74 -128
  159. data/ext/oj/hash.c +0 -131
  160. data/ext/oj/hash.h +0 -19
  161. data/ext/oj/hash_test.c +0 -491
  162. data/test/activesupport4/decoding_test.rb +0 -108
  163. data/test/activesupport4/encoding_test.rb +0 -531
  164. data/test/activesupport4/test_helper.rb +0 -41
  165. data/test/activesupport5/test_helper.rb +0 -72
  166. data/test/bar.rb +0 -35
  167. data/test/baz.rb +0 -16
  168. data/test/zoo.rb +0 -13
data/test/test_various.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
- # encoding: utf-8
2
+ # frozen_string_literal: false
3
3
 
4
- $: << File.dirname(__FILE__)
4
+ $LOAD_PATH << __dir__
5
5
 
6
6
  require 'helper'
7
7
 
8
8
  class Juice < Minitest::Test
9
- def gen_whitespaced_string(length = Random.new.rand(100))
10
- whitespace_chars = [" ", "\t", "\f", "\n", "\r"]
11
- result = ""
9
+ def gen_whitespaced_string(length=Random.new.rand(100))
10
+ whitespace_chars = [' ', "\t", "\f", "\n", "\r"]
11
+ result = ''
12
12
  length.times { result << whitespace_chars.sample }
13
13
  result
14
14
  end
@@ -32,59 +32,49 @@ class Juice < Minitest::Test
32
32
  end # Jam
33
33
 
34
34
  class Jeez < Jam
35
- def initialize(x, y)
36
- super
37
- end
38
35
 
39
- def to_json()
36
+ def to_json(*_args)
40
37
  %{{"json_class":"#{self.class}","x":#{@x},"y":#{@y}}}
41
38
  end
42
39
 
43
40
  def self.json_create(h)
44
- self.new(h['x'], h['y'])
41
+ new(h['x'], h['y'])
45
42
  end
46
43
  end # Jeez
47
44
 
48
45
  # contributed by sauliusg to fix as_json
49
46
  class Orange < Jam
50
- def initialize(x, y)
51
- super
52
- end
53
47
 
54
- def as_json()
48
+ def as_json
55
49
  { :json_class => self.class,
56
50
  :x => @x,
57
51
  :y => @y }
58
52
  end
59
53
 
60
54
  def self.json_create(h)
61
- self.new(h['x'], h['y'])
55
+ new(h['x'], h['y'])
62
56
  end
63
57
  end
64
58
 
65
59
  class Melon < Jam
66
- def initialize(x, y)
67
- super
68
- end
69
60
 
70
- def as_json(options)
61
+ def as_json(_options)
71
62
  "#{x} #{y}"
72
63
  end
73
64
 
74
65
  def self.json_create(h)
75
- self.new(h['x'], h['y'])
66
+ new(h['x'], h['y'])
76
67
  end
77
68
  end
78
69
 
79
70
  class Jazz < Jam
80
- def initialize(x, y)
81
- super
82
- end
83
- def to_hash()
71
+
72
+ def to_hash
84
73
  { 'json_class' => self.class.to_s, 'x' => @x, 'y' => @y }
85
74
  end
75
+
86
76
  def self.json_create(h)
87
- self.new(h['x'], h['y'])
77
+ new(h['x'], h['y'])
88
78
  end
89
79
  end # Jazz
90
80
 
@@ -99,7 +89,7 @@ class Juice < Minitest::Test
99
89
  def test_set_options
100
90
  orig = Oj.default_options()
101
91
  alt ={
102
- indent: " - ",
92
+ indent: ' - ',
103
93
  second_precision: 5,
104
94
  circular: true,
105
95
  class_cache: false,
@@ -115,6 +105,7 @@ class Juice < Minitest::Test
115
105
  allow_gc: false,
116
106
  quirks_mode: false,
117
107
  allow_invalid_unicode: true,
108
+ float_format: '%0.13g',
118
109
  float_precision: 13,
119
110
  mode: :strict,
120
111
  escape_mode: :ascii,
@@ -123,6 +114,8 @@ class Juice < Minitest::Test
123
114
  compat_bigdecimal: true,
124
115
  create_id: 'classy',
125
116
  create_additions: true,
117
+ cache_keys: false,
118
+ cache_str: 5,
126
119
  space: 'z',
127
120
  array_nl: 'a',
128
121
  object_nl: 'o',
@@ -137,17 +130,18 @@ class Juice < Minitest::Test
137
130
  ignore_under: true,
138
131
  trace: true,
139
132
  safe: true,
133
+ omit_null_byte: false,
140
134
  }
141
135
  Oj.default_options = alt
142
- #keys = alt.keys
143
- #Oj.default_options.keys.each { |k| puts k unless keys.include? k}
136
+ # keys = alt.keys
137
+ # Oj.default_options.keys.each { |k| puts k unless keys.include? k}
144
138
  opts = Oj.default_options()
145
- assert_equal(alt, opts);
139
+ assert_equal(alt, opts)
146
140
 
147
141
  Oj.default_options = orig # return to original
148
142
  # verify back to original
149
143
  opts = Oj.default_options()
150
- assert_equal(orig, opts);
144
+ assert_equal(orig, opts)
151
145
  end
152
146
 
153
147
  def test_nil
@@ -164,8 +158,8 @@ class Juice < Minitest::Test
164
158
 
165
159
  def test_fixnum
166
160
  dump_and_load(0, false)
167
- dump_and_load(12345, false)
168
- dump_and_load(-54321, false)
161
+ dump_and_load(12_345, false)
162
+ dump_and_load(-54_321, false)
169
163
  dump_and_load(1, false)
170
164
  end
171
165
 
@@ -173,11 +167,11 @@ class Juice < Minitest::Test
173
167
  Oj.default_options = { :float_precision => 16, :bigdecimal_load => :auto }
174
168
  n = Oj.load('0.00001234567890123456')
175
169
  assert_equal(Float, n.class)
176
- assert_equal('1.234567890123456e-05', "%0.15e" % [n])
170
+ assert_equal('1.234567890123456e-05', '%0.15e' % [n])
177
171
 
178
172
  n = Oj.load('-0.00001234567890123456')
179
173
  assert_equal(Float, n.class)
180
- assert_equal('-1.234567890123456e-05', "%0.15e" % [n])
174
+ assert_equal('-1.234567890123456e-05', '%0.15e' % [n])
181
175
 
182
176
  n = Oj.load('1000.0000123456789')
183
177
  assert_equal(BigDecimal, n.class)
@@ -225,12 +219,12 @@ class Juice < Minitest::Test
225
219
  def test_encode
226
220
  opts = Oj.default_options
227
221
  Oj.default_options = { :ascii_only => false }
228
- dump_and_load("ぴーたー", false)
222
+ dump_and_load('ぴーたー', false)
229
223
 
230
224
  Oj.default_options = { :ascii_only => true }
231
- json = Oj.dump("ぴーたー")
225
+ json = Oj.dump('ぴーたー')
232
226
  assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
233
- dump_and_load("ぴーたー", false)
227
+ dump_and_load('ぴーたー', false)
234
228
  Oj.default_options = opts
235
229
  end
236
230
 
@@ -251,7 +245,7 @@ class Juice < Minitest::Test
251
245
  assert(true)
252
246
  return
253
247
  end
254
- assert(false, "*** expected an exception")
248
+ assert(false, '*** expected an exception')
255
249
  end
256
250
 
257
251
  def test_invalid_unicode_ok
@@ -265,11 +259,11 @@ class Juice < Minitest::Test
265
259
  def test_dump_options
266
260
  json = Oj.dump({ 'a' => 1, 'b' => [true, false]},
267
261
  :mode => :compat,
268
- :indent => "--",
262
+ :indent => '--',
269
263
  :array_nl => "\n",
270
264
  :object_nl => "#\n",
271
- :space => "*",
272
- :space_before => "~")
265
+ :space => '*',
266
+ :space_before => '~')
273
267
  assert(%{{#
274
268
  --"a"~:*1,#
275
269
  --"b"~:*[
@@ -284,7 +278,6 @@ class Juice < Minitest::Test
284
278
  --],#
285
279
  --"a"~:*1#
286
280
  }} == json)
287
-
288
281
  end
289
282
 
290
283
  def test_null_char
@@ -299,6 +292,7 @@ class Juice < Minitest::Test
299
292
  dump_and_load([[nil]], false)
300
293
  dump_and_load([[nil], 58], false)
301
294
  end
295
+
302
296
  def test_array_not_closed
303
297
  begin
304
298
  Oj.load('[')
@@ -306,7 +300,7 @@ class Juice < Minitest::Test
306
300
  assert(true)
307
301
  return
308
302
  end
309
- assert(false, "*** expected an exception")
303
+ assert(false, '*** expected an exception')
310
304
  end
311
305
 
312
306
  # multiple JSON in one string
@@ -317,7 +311,7 @@ class Juice < Minitest::Test
317
311
  }
318
312
  results = []
319
313
  Oj.load(json, :mode => :strict) { |x, start, len| results << [x, start, len] }
320
- assert_equal([[{"a"=>1}, 0, 7], [[1,2], 7, 6], [[3,4], 13, 5], [{"b"=>2}, 18, 8]], results)
314
+ assert_equal([[{'a'=>1}, 0, 7], [[1, 2], 7, 6], [[3, 4], 13, 5], [{'b'=>2}, 18, 8]], results)
321
315
  end
322
316
 
323
317
  def test_multiple_json_no_callback
@@ -337,27 +331,39 @@ class Juice < Minitest::Test
337
331
  out = Oj.dump(hash)
338
332
  assert_equal(json, out)
339
333
  end
334
+
340
335
  def test_escapes_entities_by_default_when_configured_to_do_so
341
- hash = {'key' => "I <3 this"}
336
+ hash = {'key' => 'I <3 this'}
342
337
  Oj.default_options = {:escape_mode => :xss_safe}
343
338
  out = Oj.dump hash
344
339
  assert_equal(%{{"key":"I \\u003c3 this"}}, out)
345
340
  end
341
+
342
+ def test_escapes_slashes_by_default_when_configured_to_do_so
343
+ hash = {'key' => 'I <3 this </script>'}
344
+ Oj.default_options = {:escape_mode => :slash}
345
+ out = Oj.dump hash
346
+ assert_equal(%{{"key":"I <3 this <\\/script>"}}, out)
347
+ end
348
+
346
349
  def test_escapes_entities_when_asked_to
347
- hash = {'key' => "I <3 this"}
350
+ hash = {'key' => 'I <3 this'}
348
351
  out = Oj.dump(hash, :escape_mode => :xss_safe)
349
352
  assert_equal(%{{"key":"I \\u003c3 this"}}, out)
350
353
  end
354
+
351
355
  def test_does_not_escape_entities_when_not_asked_to
352
- hash = {'key' => "I <3 this"}
356
+ hash = {'key' => 'I <3 this'}
353
357
  out = Oj.dump(hash, :escape_mode => :json)
354
358
  assert_equal(%{{"key":"I <3 this"}}, out)
355
359
  end
360
+
356
361
  def test_escapes_common_xss_vectors
357
- hash = {'key' => "<script>alert(123) && formatHD()</script>"}
362
+ hash = {'key' => '<script>alert(123) && formatHD()</script>'}
358
363
  out = Oj.dump(hash, :escape_mode => :xss_safe)
359
364
  assert_equal(%{{"key":"\\u003cscript\\u003ealert(123) \\u0026\\u0026 formatHD()\\u003c\\/script\\u003e"}}, out)
360
365
  end
366
+
361
367
  def test_escape_newline_by_default
362
368
  Oj.default_options = { :escape_mode => :json }
363
369
  json = %{["one","two\\n2"]}
@@ -365,6 +371,7 @@ class Juice < Minitest::Test
365
371
  out = Oj.dump(x)
366
372
  assert_equal(json, out)
367
373
  end
374
+
368
375
  def test_does_not_escape_newline
369
376
  Oj.default_options = { :escape_mode => :newline }
370
377
  json = %{["one","two\n2"]}
@@ -372,6 +379,7 @@ class Juice < Minitest::Test
372
379
  out = Oj.dump(x)
373
380
  assert_equal(json, out)
374
381
  end
382
+
375
383
  def test_dump_invalid_utf8
376
384
  Oj.default_options = { :escape_mode => :ascii }
377
385
  assert_raises(EncodingError) {
@@ -393,14 +401,14 @@ class Juice < Minitest::Test
393
401
  end
394
402
 
395
403
  def test_time_neg
396
- t = Time.parse("1900-01-01 00:18:59 UTC")
404
+ t = Time.parse('1900-01-01 00:18:59 UTC')
397
405
  json = Oj.dump(t, :mode => :custom, :time_format => :unix)
398
406
  assert_equal('-2208987661.000000000', json)
399
407
  end
400
408
 
401
409
  def test_time_years
402
410
  (-2020..2020).each { |year|
403
- s = "%04d-03-01T15:14:13Z" % [year]
411
+ s = '%04d-03-01T15:14:13Z' % [year]
404
412
  json = Oj.dump(Time.parse(s), mode: :custom, time_format: :xmlschema, second_precision: -1)
405
413
  assert_equal(s, json[1..-2])
406
414
 
@@ -409,6 +417,11 @@ class Juice < Minitest::Test
409
417
  }
410
418
  end
411
419
 
420
+ def test_dump_float
421
+ json = Oj.dump(1.23e-2, :mode => :null, :float_format => '%0.4f')
422
+ assert_equal('0.0123', json)
423
+ end
424
+
412
425
  # Class
413
426
  def test_class_null
414
427
  json = Oj.dump(Juice, :mode => :null)
@@ -429,7 +442,7 @@ class Juice < Minitest::Test
429
442
  assert(true)
430
443
  return
431
444
  end
432
- assert(false, "*** expected an exception")
445
+ assert(false, '*** expected an exception')
433
446
  end
434
447
 
435
448
  def test_hash_not_closed
@@ -439,7 +452,7 @@ class Juice < Minitest::Test
439
452
  assert(true)
440
453
  return
441
454
  end
442
- assert(false, "*** expected an exception")
455
+ assert(false, '*** expected an exception')
443
456
  end
444
457
 
445
458
  # Object with to_json()
@@ -449,7 +462,7 @@ class Juice < Minitest::Test
449
462
  assert_equal('null', json)
450
463
  end
451
464
 
452
- # Object with to_hash()
465
+ # Object with to_hash()
453
466
  def test_to_hash_object_null
454
467
  obj = Jazz.new(true, 58)
455
468
  json = Oj.dump(obj, :mode => :null)
@@ -497,7 +510,7 @@ class Juice < Minitest::Test
497
510
 
498
511
  def test_infinity
499
512
  n = Oj.load('Infinity', :mode => :object)
500
- assert_equal(BigDecimal('Infinity').to_f, n);
513
+ assert_equal(BigDecimal('Infinity').to_f, n)
501
514
  x = Oj.load('Infinity', :mode => :compat)
502
515
  assert_equal('Infinity', x.to_s)
503
516
  end
@@ -522,27 +535,27 @@ class Juice < Minitest::Test
522
535
  "y":58 }}
523
536
  obj = Oj.load(json, :mode => :object, :auto_define => true)
524
537
  assert_equal('Juice::Jem', obj.class.name)
525
- assert_equal(true, obj.x)
538
+ assert(obj.x)
526
539
  assert_equal(58, obj.y)
527
540
  end
528
541
 
529
- # Stream Deeply Nested
542
+ # Stream Deeply Nested
530
543
  def test_deep_nest_dump
531
544
  begin
532
545
  a = []
533
- 10000.times { a << [a] }
546
+ 10_000.times { a << [a] }
534
547
  Oj.dump(a)
535
548
  rescue Exception
536
549
  assert(true)
537
550
  return
538
551
  end
539
- assert(false, "*** expected an exception")
552
+ assert(false, '*** expected an exception')
540
553
  end
541
554
 
542
- # Stream IO
555
+ # Stream IO
543
556
  def test_io_string
544
557
  src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
545
- output = StringIO.open("", "w+")
558
+ output = StringIO.open('', 'w+')
546
559
  Oj.to_stream(output, src)
547
560
 
548
561
  input = StringIO.new(output.string())
@@ -552,8 +565,8 @@ class Juice < Minitest::Test
552
565
 
553
566
  def test_io_file
554
567
  src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
555
- filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
556
- File.open(filename, "w") { |f|
568
+ filename = File.join(__dir__, 'open_file_test.json')
569
+ File.open(filename, 'w') { |f|
557
570
  Oj.to_stream(f, src)
558
571
  }
559
572
  f = File.new(filename)
@@ -562,7 +575,31 @@ class Juice < Minitest::Test
562
575
  assert_equal(src, obj)
563
576
  end
564
577
 
565
- # comments
578
+ def test_io_stream
579
+ skip 'needs fork' unless Process.respond_to?(:fork)
580
+
581
+ IO.pipe do |r, w|
582
+ if fork
583
+ r.close
584
+ # w.nonblock = false
585
+ a = []
586
+ 10_000.times do |i|
587
+ a << i
588
+ end
589
+ Oj.to_stream(w, a, indent: 2)
590
+ w.close
591
+ else
592
+ w.close
593
+ sleep(0.1) # to force a busy
594
+ cnt = 0
595
+ r.each_line { cnt += 1 }
596
+ r.close
597
+ Process.exit(0)
598
+ end
599
+ end
600
+ end
601
+
602
+ # comments
566
603
  def test_comment_slash
567
604
  json = %{{
568
605
  "x":true,//three
@@ -606,7 +643,7 @@ class Juice < Minitest::Test
606
643
  assert(true)
607
644
  return
608
645
  end
609
- assert(false, "*** expected an exception")
646
+ assert(false, '*** expected an exception')
610
647
  end
611
648
 
612
649
  def test_nilnil_true
@@ -629,27 +666,27 @@ class Juice < Minitest::Test
629
666
  assert(Oj::ParseError == e.class || EncodingError == e.class)
630
667
  return
631
668
  end
632
- assert(false, "*** expected an exception")
669
+ assert(false, '*** expected an exception')
633
670
  end
634
671
 
635
672
  def test_quirks_null_mode
636
- assert_raises(Oj::ParseError) { Oj.load("null", :quirks_mode => false) }
637
- assert_nil(Oj.load("null", :quirks_mode => true))
673
+ assert_raises(Oj::ParseError) { Oj.load('null', :quirks_mode => false) }
674
+ assert_nil(Oj.load('null', :quirks_mode => true))
638
675
  end
639
676
 
640
677
  def test_quirks_bool_mode
641
- assert_raises(Oj::ParseError) { Oj.load("true", :quirks_mode => false) }
642
- assert_equal(true, Oj.load("true", :quirks_mode => true))
678
+ assert_raises(Oj::ParseError) { Oj.load('true', :quirks_mode => false) }
679
+ assert(Oj.load('true', :quirks_mode => true))
643
680
  end
644
681
 
645
682
  def test_quirks_number_mode
646
- assert_raises(Oj::ParseError) { Oj.load("123", :quirks_mode => false) }
647
- assert_equal(123, Oj.load("123", :quirks_mode => true))
683
+ assert_raises(Oj::ParseError) { Oj.load('123', :quirks_mode => false) }
684
+ assert_equal(123, Oj.load('123', :quirks_mode => true))
648
685
  end
649
686
 
650
687
  def test_quirks_decimal_mode
651
- assert_raises(Oj::ParseError) { Oj.load("123.45", :quirks_mode => false) }
652
- assert_equal(123.45, Oj.load("123.45", :quirks_mode => true))
688
+ assert_raises(Oj::ParseError) { Oj.load('123.45', :quirks_mode => false) }
689
+ assert_in_delta(123.45, Oj.load('123.45', :quirks_mode => true))
653
690
  end
654
691
 
655
692
  def test_quirks_string_mode
@@ -661,35 +698,33 @@ class Juice < Minitest::Test
661
698
  msg = ''
662
699
  assert_raises(Oj::ParseError) {
663
700
  begin
664
- Oj.load(%|{
701
+ Oj.load(%|{
665
702
  "first": [
666
703
  1, 2, { "third": 123x }
667
704
  ]
668
705
  }|)
669
706
  rescue Oj::ParseError => e
670
- msg = e.message
671
- raise e
707
+ msg = e.message
708
+ raise e
672
709
  end
673
710
  }
674
711
  assert_equal('after first[2].third', msg.split('(')[1].split(')')[0])
675
712
  end
676
713
 
677
714
  def test_bad_bignum
678
- if '2.4.0' < RUBY_VERSION
679
- assert_raises Oj::ParseError do
680
- Oj.load(%|{ "big": -e123456789 }|, mode: :strict)
681
- end
715
+ assert_raises Oj::ParseError do
716
+ Oj.load(%|{ "big": -e123456789 }|, mode: :strict)
682
717
  end
683
718
  end
684
719
 
685
720
  def test_quirks_array_mode
686
- assert_equal([], Oj.load("[]", :quirks_mode => false))
687
- assert_equal([], Oj.load("[]", :quirks_mode => true))
721
+ assert_empty(Oj.load('[]', :quirks_mode => false))
722
+ assert_empty(Oj.load('[]', :quirks_mode => true))
688
723
  end
689
724
 
690
725
  def test_quirks_object_mode
691
- assert_equal({}, Oj.load("{}", :quirks_mode => false))
692
- assert_equal({}, Oj.load("{}", :quirks_mode => true))
726
+ assert_empty(Oj.load('{}', :quirks_mode => false))
727
+ assert_empty(Oj.load('{}', :quirks_mode => true))
693
728
  end
694
729
 
695
730
  def test_omit_nil
@@ -705,6 +740,52 @@ class Juice < Minitest::Test
705
740
  assert_equal(%|{"x":{"a":1}}|, json)
706
741
  end
707
742
 
743
+ def test_omit_null_byte
744
+ Oj.default_options = { :omit_null_byte => true }
745
+
746
+ json = Oj.dump({ "fo\x00o" => "b\x00ar" })
747
+ assert_equal(%|{"foo":"bar"}|, json)
748
+
749
+ json = Oj.dump({ "foo\x00" => "\x00bar" })
750
+ assert_equal(%|{"foo":"bar"}|, json)
751
+
752
+ json = Oj.dump({ "\x00foo" => "bar\x00" })
753
+ assert_equal(%|{"foo":"bar"}|, json)
754
+
755
+ json = Oj.dump({ "fo\0o" => "ba\0r" })
756
+ assert_equal(%|{"foo":"bar"}|, json)
757
+
758
+ json = Oj.dump({ "foo\0" => "\0bar" })
759
+ assert_equal(%|{"foo":"bar"}|, json)
760
+
761
+ json = Oj.dump({ "\0foo" => "bar\0" })
762
+ assert_equal(%|{"foo":"bar"}|, json)
763
+
764
+ json = Oj.dump({ "fo\u0000o" => "ba\u0000r" })
765
+ assert_equal(%|{"foo":"bar"}|, json)
766
+
767
+ json = Oj.dump({ "foo\u0000" => "\u0000bar" })
768
+ assert_equal(%|{"foo":"bar"}|, json)
769
+
770
+ json = Oj.dump({ "\u0000foo" => "bar\u0000" })
771
+ assert_equal(%|{"foo":"bar"}|, json)
772
+
773
+ json = Oj.dump({ "\x00foo" => "bar\x00" }, :omit_null_byte => false)
774
+ assert_equal(%|{"\\u0000foo":"bar\\u0000"}|, json)
775
+
776
+ # no change from default set earlier so :omit_null_byte is still true
777
+ json = Oj.dump({ "\x00foo" => "bar\x00" }, :omit_null_byte => nil)
778
+ assert_equal(%|{"foo":"bar"}|, json)
779
+
780
+ Oj.default_options = { :omit_null_byte => false }
781
+
782
+ json = Oj.dump({ "\x00foo" => "bar\x00" })
783
+ assert_equal(%|{"\\u0000foo":"bar\\u0000"}|, json)
784
+
785
+ json = Oj.dump({ "\x00foo" => "bar\x00" }, :omit_null_byte => true)
786
+ assert_equal(%|{"foo":"bar"}|, json)
787
+ end
788
+
708
789
  def dump_and_load(obj, trace=false)
709
790
  json = Oj.dump(obj, :indent => 2)
710
791
  puts json if trace