oj 3.13.9 → 3.16.1

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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +101 -0
  3. data/README.md +13 -2
  4. data/ext/oj/buf.h +11 -6
  5. data/ext/oj/cache.c +25 -24
  6. data/ext/oj/cache8.c +10 -9
  7. data/ext/oj/circarray.c +8 -6
  8. data/ext/oj/circarray.h +2 -2
  9. data/ext/oj/code.c +19 -33
  10. data/ext/oj/code.h +2 -2
  11. data/ext/oj/compat.c +20 -60
  12. data/ext/oj/custom.c +76 -155
  13. data/ext/oj/debug.c +3 -9
  14. data/ext/oj/dump.c +203 -213
  15. data/ext/oj/dump.h +26 -12
  16. data/ext/oj/dump_compat.c +565 -642
  17. data/ext/oj/dump_leaf.c +17 -63
  18. data/ext/oj/dump_object.c +59 -181
  19. data/ext/oj/dump_strict.c +24 -48
  20. data/ext/oj/encoder.c +43 -0
  21. data/ext/oj/err.c +2 -13
  22. data/ext/oj/err.h +9 -12
  23. data/ext/oj/extconf.rb +18 -7
  24. data/ext/oj/fast.c +83 -108
  25. data/ext/oj/intern.c +52 -50
  26. data/ext/oj/intern.h +4 -8
  27. data/ext/oj/mem.c +318 -0
  28. data/ext/oj/mem.h +53 -0
  29. data/ext/oj/mimic_json.c +104 -81
  30. data/ext/oj/object.c +50 -67
  31. data/ext/oj/odd.c +89 -67
  32. data/ext/oj/odd.h +15 -15
  33. data/ext/oj/oj.c +171 -106
  34. data/ext/oj/oj.h +96 -74
  35. data/ext/oj/parse.c +169 -189
  36. data/ext/oj/parse.h +23 -24
  37. data/ext/oj/parser.c +89 -34
  38. data/ext/oj/parser.h +20 -9
  39. data/ext/oj/rails.c +86 -151
  40. data/ext/oj/rails.h +1 -1
  41. data/ext/oj/reader.c +12 -15
  42. data/ext/oj/reader.h +4 -2
  43. data/ext/oj/resolve.c +3 -4
  44. data/ext/oj/rxclass.c +6 -5
  45. data/ext/oj/rxclass.h +1 -1
  46. data/ext/oj/saj.c +21 -32
  47. data/ext/oj/saj2.c +329 -93
  48. data/ext/oj/saj2.h +23 -0
  49. data/ext/oj/scp.c +3 -14
  50. data/ext/oj/sparse.c +26 -70
  51. data/ext/oj/stream_writer.c +12 -22
  52. data/ext/oj/strict.c +20 -52
  53. data/ext/oj/string_writer.c +21 -22
  54. data/ext/oj/trace.h +31 -4
  55. data/ext/oj/usual.c +105 -150
  56. data/ext/oj/usual.h +68 -0
  57. data/ext/oj/util.h +1 -1
  58. data/ext/oj/val_stack.c +1 -1
  59. data/ext/oj/val_stack.h +8 -7
  60. data/ext/oj/validate.c +21 -26
  61. data/ext/oj/wab.c +32 -69
  62. data/lib/oj/active_support_helper.rb +1 -3
  63. data/lib/oj/bag.rb +7 -1
  64. data/lib/oj/easy_hash.rb +4 -5
  65. data/lib/oj/error.rb +0 -1
  66. data/lib/oj/json.rb +162 -150
  67. data/lib/oj/mimic.rb +6 -2
  68. data/lib/oj/saj.rb +20 -6
  69. data/lib/oj/state.rb +9 -6
  70. data/lib/oj/version.rb +1 -2
  71. data/lib/oj.rb +2 -0
  72. data/pages/Compatibility.md +1 -1
  73. data/pages/InstallOptions.md +20 -0
  74. data/pages/JsonGem.md +15 -0
  75. data/pages/Modes.md +6 -3
  76. data/pages/Options.md +10 -0
  77. data/pages/Rails.md +12 -0
  78. data/test/_test_active.rb +8 -9
  79. data/test/_test_active_mimic.rb +7 -8
  80. data/test/_test_mimic_rails.rb +17 -20
  81. data/test/activerecord/result_test.rb +5 -6
  82. data/test/{activesupport5 → activesupport7}/abstract_unit.rb +16 -12
  83. data/test/{activesupport5 → activesupport7}/decoding_test.rb +2 -10
  84. data/test/{activesupport5 → activesupport7}/encoding_test.rb +20 -34
  85. data/test/{activesupport5 → activesupport7}/encoding_test_cases.rb +6 -0
  86. data/test/{activesupport5 → activesupport7}/time_zone_test_helpers.rb +8 -0
  87. data/test/files.rb +15 -15
  88. data/test/foo.rb +15 -15
  89. data/test/helper.rb +11 -8
  90. data/test/isolated/shared.rb +3 -2
  91. data/test/json_gem/json_addition_test.rb +2 -2
  92. data/test/json_gem/json_common_interface_test.rb +8 -6
  93. data/test/json_gem/json_encoding_test.rb +0 -0
  94. data/test/json_gem/json_ext_parser_test.rb +1 -0
  95. data/test/json_gem/json_fixtures_test.rb +3 -2
  96. data/test/json_gem/json_generator_test.rb +49 -37
  97. data/test/json_gem/json_generic_object_test.rb +11 -11
  98. data/test/json_gem/json_parser_test.rb +54 -47
  99. data/test/json_gem/json_string_matching_test.rb +9 -9
  100. data/test/json_gem/test_helper.rb +7 -3
  101. data/test/mem.rb +13 -12
  102. data/test/perf.rb +21 -26
  103. data/test/perf_compat.rb +31 -33
  104. data/test/perf_dump.rb +50 -0
  105. data/test/perf_fast.rb +80 -82
  106. data/test/perf_file.rb +27 -29
  107. data/test/perf_object.rb +65 -69
  108. data/test/perf_once.rb +12 -11
  109. data/test/perf_parser.rb +42 -48
  110. data/test/perf_saj.rb +46 -54
  111. data/test/perf_scp.rb +57 -69
  112. data/test/perf_simple.rb +41 -39
  113. data/test/perf_strict.rb +68 -70
  114. data/test/perf_wab.rb +67 -69
  115. data/test/prec.rb +3 -3
  116. data/test/sample/change.rb +0 -1
  117. data/test/sample/dir.rb +0 -1
  118. data/test/sample/doc.rb +0 -1
  119. data/test/sample/file.rb +0 -1
  120. data/test/sample/group.rb +0 -1
  121. data/test/sample/hasprops.rb +0 -1
  122. data/test/sample/layer.rb +0 -1
  123. data/test/sample/rect.rb +0 -1
  124. data/test/sample/shape.rb +0 -1
  125. data/test/sample/text.rb +0 -1
  126. data/test/sample.rb +16 -16
  127. data/test/sample_json.rb +8 -8
  128. data/test/test_compat.rb +95 -43
  129. data/test/test_custom.rb +72 -51
  130. data/test/test_debian.rb +7 -10
  131. data/test/test_fast.rb +102 -87
  132. data/test/test_file.rb +41 -30
  133. data/test/test_gc.rb +16 -5
  134. data/test/test_generate.rb +5 -5
  135. data/test/test_hash.rb +4 -4
  136. data/test/test_integer_range.rb +9 -9
  137. data/test/test_null.rb +20 -20
  138. data/test/test_object.rb +85 -96
  139. data/test/test_parser.rb +6 -22
  140. data/test/test_parser_debug.rb +27 -0
  141. data/test/test_parser_saj.rb +115 -23
  142. data/test/test_parser_usual.rb +6 -6
  143. data/test/test_rails.rb +2 -2
  144. data/test/test_saj.rb +10 -8
  145. data/test/test_scp.rb +37 -39
  146. data/test/test_strict.rb +40 -32
  147. data/test/test_various.rb +163 -84
  148. data/test/test_wab.rb +48 -44
  149. data/test/test_writer.rb +47 -47
  150. data/test/tests.rb +13 -5
  151. data/test/tests_mimic.rb +12 -3
  152. data/test/tests_mimic_addition.rb +12 -3
  153. metadata +34 -144
  154. data/test/activesupport4/decoding_test.rb +0 -108
  155. data/test/activesupport4/encoding_test.rb +0 -531
  156. data/test/activesupport4/test_helper.rb +0 -41
  157. data/test/activesupport5/test_helper.rb +0 -72
  158. data/test/bar.rb +0 -16
  159. data/test/baz.rb +0 -16
  160. data/test/bug.rb +0 -16
  161. 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,
@@ -139,17 +130,18 @@ class Juice < Minitest::Test
139
130
  ignore_under: true,
140
131
  trace: true,
141
132
  safe: true,
133
+ omit_null_byte: false,
142
134
  }
143
135
  Oj.default_options = alt
144
- #keys = alt.keys
145
- #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}
146
138
  opts = Oj.default_options()
147
- assert_equal(alt, opts);
139
+ assert_equal(alt, opts)
148
140
 
149
141
  Oj.default_options = orig # return to original
150
142
  # verify back to original
151
143
  opts = Oj.default_options()
152
- assert_equal(orig, opts);
144
+ assert_equal(orig, opts)
153
145
  end
154
146
 
155
147
  def test_nil
@@ -166,8 +158,8 @@ class Juice < Minitest::Test
166
158
 
167
159
  def test_fixnum
168
160
  dump_and_load(0, false)
169
- dump_and_load(12345, false)
170
- dump_and_load(-54321, false)
161
+ dump_and_load(12_345, false)
162
+ dump_and_load(-54_321, false)
171
163
  dump_and_load(1, false)
172
164
  end
173
165
 
@@ -175,11 +167,11 @@ class Juice < Minitest::Test
175
167
  Oj.default_options = { :float_precision => 16, :bigdecimal_load => :auto }
176
168
  n = Oj.load('0.00001234567890123456')
177
169
  assert_equal(Float, n.class)
178
- assert_equal('1.234567890123456e-05', "%0.15e" % [n])
170
+ assert_equal('1.234567890123456e-05', '%0.15e' % [n])
179
171
 
180
172
  n = Oj.load('-0.00001234567890123456')
181
173
  assert_equal(Float, n.class)
182
- assert_equal('-1.234567890123456e-05', "%0.15e" % [n])
174
+ assert_equal('-1.234567890123456e-05', '%0.15e' % [n])
183
175
 
184
176
  n = Oj.load('1000.0000123456789')
185
177
  assert_equal(BigDecimal, n.class)
@@ -227,12 +219,12 @@ class Juice < Minitest::Test
227
219
  def test_encode
228
220
  opts = Oj.default_options
229
221
  Oj.default_options = { :ascii_only => false }
230
- dump_and_load("ぴーたー", false)
222
+ dump_and_load('ぴーたー', false)
231
223
 
232
224
  Oj.default_options = { :ascii_only => true }
233
- json = Oj.dump("ぴーたー")
225
+ json = Oj.dump('ぴーたー')
234
226
  assert_equal(%{"\\u3074\\u30fc\\u305f\\u30fc"}, json)
235
- dump_and_load("ぴーたー", false)
227
+ dump_and_load('ぴーたー', false)
236
228
  Oj.default_options = opts
237
229
  end
238
230
 
@@ -253,7 +245,7 @@ class Juice < Minitest::Test
253
245
  assert(true)
254
246
  return
255
247
  end
256
- assert(false, "*** expected an exception")
248
+ assert(false, '*** expected an exception')
257
249
  end
258
250
 
259
251
  def test_invalid_unicode_ok
@@ -267,11 +259,11 @@ class Juice < Minitest::Test
267
259
  def test_dump_options
268
260
  json = Oj.dump({ 'a' => 1, 'b' => [true, false]},
269
261
  :mode => :compat,
270
- :indent => "--",
262
+ :indent => '--',
271
263
  :array_nl => "\n",
272
264
  :object_nl => "#\n",
273
- :space => "*",
274
- :space_before => "~")
265
+ :space => '*',
266
+ :space_before => '~')
275
267
  assert(%{{#
276
268
  --"a"~:*1,#
277
269
  --"b"~:*[
@@ -286,7 +278,6 @@ class Juice < Minitest::Test
286
278
  --],#
287
279
  --"a"~:*1#
288
280
  }} == json)
289
-
290
281
  end
291
282
 
292
283
  def test_null_char
@@ -301,6 +292,7 @@ class Juice < Minitest::Test
301
292
  dump_and_load([[nil]], false)
302
293
  dump_and_load([[nil], 58], false)
303
294
  end
295
+
304
296
  def test_array_not_closed
305
297
  begin
306
298
  Oj.load('[')
@@ -308,7 +300,7 @@ class Juice < Minitest::Test
308
300
  assert(true)
309
301
  return
310
302
  end
311
- assert(false, "*** expected an exception")
303
+ assert(false, '*** expected an exception')
312
304
  end
313
305
 
314
306
  # multiple JSON in one string
@@ -319,7 +311,7 @@ class Juice < Minitest::Test
319
311
  }
320
312
  results = []
321
313
  Oj.load(json, :mode => :strict) { |x, start, len| results << [x, start, len] }
322
- 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)
323
315
  end
324
316
 
325
317
  def test_multiple_json_no_callback
@@ -339,27 +331,39 @@ class Juice < Minitest::Test
339
331
  out = Oj.dump(hash)
340
332
  assert_equal(json, out)
341
333
  end
334
+
342
335
  def test_escapes_entities_by_default_when_configured_to_do_so
343
- hash = {'key' => "I <3 this"}
336
+ hash = {'key' => 'I <3 this'}
344
337
  Oj.default_options = {:escape_mode => :xss_safe}
345
338
  out = Oj.dump hash
346
339
  assert_equal(%{{"key":"I \\u003c3 this"}}, out)
347
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
+
348
349
  def test_escapes_entities_when_asked_to
349
- hash = {'key' => "I <3 this"}
350
+ hash = {'key' => 'I <3 this'}
350
351
  out = Oj.dump(hash, :escape_mode => :xss_safe)
351
352
  assert_equal(%{{"key":"I \\u003c3 this"}}, out)
352
353
  end
354
+
353
355
  def test_does_not_escape_entities_when_not_asked_to
354
- hash = {'key' => "I <3 this"}
356
+ hash = {'key' => 'I <3 this'}
355
357
  out = Oj.dump(hash, :escape_mode => :json)
356
358
  assert_equal(%{{"key":"I <3 this"}}, out)
357
359
  end
360
+
358
361
  def test_escapes_common_xss_vectors
359
- hash = {'key' => "<script>alert(123) && formatHD()</script>"}
362
+ hash = {'key' => '<script>alert(123) && formatHD()</script>'}
360
363
  out = Oj.dump(hash, :escape_mode => :xss_safe)
361
364
  assert_equal(%{{"key":"\\u003cscript\\u003ealert(123) \\u0026\\u0026 formatHD()\\u003c\\/script\\u003e"}}, out)
362
365
  end
366
+
363
367
  def test_escape_newline_by_default
364
368
  Oj.default_options = { :escape_mode => :json }
365
369
  json = %{["one","two\\n2"]}
@@ -367,6 +371,7 @@ class Juice < Minitest::Test
367
371
  out = Oj.dump(x)
368
372
  assert_equal(json, out)
369
373
  end
374
+
370
375
  def test_does_not_escape_newline
371
376
  Oj.default_options = { :escape_mode => :newline }
372
377
  json = %{["one","two\n2"]}
@@ -374,6 +379,7 @@ class Juice < Minitest::Test
374
379
  out = Oj.dump(x)
375
380
  assert_equal(json, out)
376
381
  end
382
+
377
383
  def test_dump_invalid_utf8
378
384
  Oj.default_options = { :escape_mode => :ascii }
379
385
  assert_raises(EncodingError) {
@@ -395,14 +401,14 @@ class Juice < Minitest::Test
395
401
  end
396
402
 
397
403
  def test_time_neg
398
- t = Time.parse("1900-01-01 00:18:59 UTC")
404
+ t = Time.parse('1900-01-01 00:18:59 UTC')
399
405
  json = Oj.dump(t, :mode => :custom, :time_format => :unix)
400
406
  assert_equal('-2208987661.000000000', json)
401
407
  end
402
408
 
403
409
  def test_time_years
404
410
  (-2020..2020).each { |year|
405
- s = "%04d-03-01T15:14:13Z" % [year]
411
+ s = '%04d-03-01T15:14:13Z' % [year]
406
412
  json = Oj.dump(Time.parse(s), mode: :custom, time_format: :xmlschema, second_precision: -1)
407
413
  assert_equal(s, json[1..-2])
408
414
 
@@ -411,6 +417,11 @@ class Juice < Minitest::Test
411
417
  }
412
418
  end
413
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
+
414
425
  # Class
415
426
  def test_class_null
416
427
  json = Oj.dump(Juice, :mode => :null)
@@ -431,7 +442,7 @@ class Juice < Minitest::Test
431
442
  assert(true)
432
443
  return
433
444
  end
434
- assert(false, "*** expected an exception")
445
+ assert(false, '*** expected an exception')
435
446
  end
436
447
 
437
448
  def test_hash_not_closed
@@ -441,7 +452,7 @@ class Juice < Minitest::Test
441
452
  assert(true)
442
453
  return
443
454
  end
444
- assert(false, "*** expected an exception")
455
+ assert(false, '*** expected an exception')
445
456
  end
446
457
 
447
458
  # Object with to_json()
@@ -451,7 +462,7 @@ class Juice < Minitest::Test
451
462
  assert_equal('null', json)
452
463
  end
453
464
 
454
- # Object with to_hash()
465
+ # Object with to_hash()
455
466
  def test_to_hash_object_null
456
467
  obj = Jazz.new(true, 58)
457
468
  json = Oj.dump(obj, :mode => :null)
@@ -499,7 +510,7 @@ class Juice < Minitest::Test
499
510
 
500
511
  def test_infinity
501
512
  n = Oj.load('Infinity', :mode => :object)
502
- assert_equal(BigDecimal('Infinity').to_f, n);
513
+ assert_equal(BigDecimal('Infinity').to_f, n)
503
514
  x = Oj.load('Infinity', :mode => :compat)
504
515
  assert_equal('Infinity', x.to_s)
505
516
  end
@@ -524,27 +535,27 @@ class Juice < Minitest::Test
524
535
  "y":58 }}
525
536
  obj = Oj.load(json, :mode => :object, :auto_define => true)
526
537
  assert_equal('Juice::Jem', obj.class.name)
527
- assert_equal(true, obj.x)
538
+ assert(obj.x)
528
539
  assert_equal(58, obj.y)
529
540
  end
530
541
 
531
- # Stream Deeply Nested
542
+ # Stream Deeply Nested
532
543
  def test_deep_nest_dump
533
544
  begin
534
545
  a = []
535
- 10000.times { a << [a] }
546
+ 10_000.times { a << [a] }
536
547
  Oj.dump(a)
537
548
  rescue Exception
538
549
  assert(true)
539
550
  return
540
551
  end
541
- assert(false, "*** expected an exception")
552
+ assert(false, '*** expected an exception')
542
553
  end
543
554
 
544
- # Stream IO
555
+ # Stream IO
545
556
  def test_io_string
546
557
  src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
547
- output = StringIO.open("", "w+")
558
+ output = StringIO.open('', 'w+')
548
559
  Oj.to_stream(output, src)
549
560
 
550
561
  input = StringIO.new(output.string())
@@ -554,8 +565,8 @@ class Juice < Minitest::Test
554
565
 
555
566
  def test_io_file
556
567
  src = { 'x' => true, 'y' => 58, 'z' => [1, 2, 3]}
557
- filename = File.join(File.dirname(__FILE__), 'open_file_test.json')
558
- File.open(filename, "w") { |f|
568
+ filename = File.join(__dir__, 'open_file_test.json')
569
+ File.open(filename, 'w') { |f|
559
570
  Oj.to_stream(f, src)
560
571
  }
561
572
  f = File.new(filename)
@@ -564,7 +575,31 @@ class Juice < Minitest::Test
564
575
  assert_equal(src, obj)
565
576
  end
566
577
 
567
- # 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
568
603
  def test_comment_slash
569
604
  json = %{{
570
605
  "x":true,//three
@@ -608,7 +643,7 @@ class Juice < Minitest::Test
608
643
  assert(true)
609
644
  return
610
645
  end
611
- assert(false, "*** expected an exception")
646
+ assert(false, '*** expected an exception')
612
647
  end
613
648
 
614
649
  def test_nilnil_true
@@ -631,27 +666,27 @@ class Juice < Minitest::Test
631
666
  assert(Oj::ParseError == e.class || EncodingError == e.class)
632
667
  return
633
668
  end
634
- assert(false, "*** expected an exception")
669
+ assert(false, '*** expected an exception')
635
670
  end
636
671
 
637
672
  def test_quirks_null_mode
638
- assert_raises(Oj::ParseError) { Oj.load("null", :quirks_mode => false) }
639
- 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))
640
675
  end
641
676
 
642
677
  def test_quirks_bool_mode
643
- assert_raises(Oj::ParseError) { Oj.load("true", :quirks_mode => false) }
644
- 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))
645
680
  end
646
681
 
647
682
  def test_quirks_number_mode
648
- assert_raises(Oj::ParseError) { Oj.load("123", :quirks_mode => false) }
649
- 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))
650
685
  end
651
686
 
652
687
  def test_quirks_decimal_mode
653
- assert_raises(Oj::ParseError) { Oj.load("123.45", :quirks_mode => false) }
654
- 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))
655
690
  end
656
691
 
657
692
  def test_quirks_string_mode
@@ -663,35 +698,33 @@ class Juice < Minitest::Test
663
698
  msg = ''
664
699
  assert_raises(Oj::ParseError) {
665
700
  begin
666
- Oj.load(%|{
701
+ Oj.load(%|{
667
702
  "first": [
668
703
  1, 2, { "third": 123x }
669
704
  ]
670
705
  }|)
671
706
  rescue Oj::ParseError => e
672
- msg = e.message
673
- raise e
707
+ msg = e.message
708
+ raise e
674
709
  end
675
710
  }
676
711
  assert_equal('after first[2].third', msg.split('(')[1].split(')')[0])
677
712
  end
678
713
 
679
714
  def test_bad_bignum
680
- if '2.4.0' < RUBY_VERSION
681
- assert_raises Oj::ParseError do
682
- Oj.load(%|{ "big": -e123456789 }|, mode: :strict)
683
- end
715
+ assert_raises Oj::ParseError do
716
+ Oj.load(%|{ "big": -e123456789 }|, mode: :strict)
684
717
  end
685
718
  end
686
719
 
687
720
  def test_quirks_array_mode
688
- assert_equal([], Oj.load("[]", :quirks_mode => false))
689
- assert_equal([], Oj.load("[]", :quirks_mode => true))
721
+ assert_empty(Oj.load('[]', :quirks_mode => false))
722
+ assert_empty(Oj.load('[]', :quirks_mode => true))
690
723
  end
691
724
 
692
725
  def test_quirks_object_mode
693
- assert_equal({}, Oj.load("{}", :quirks_mode => false))
694
- assert_equal({}, Oj.load("{}", :quirks_mode => true))
726
+ assert_empty(Oj.load('{}', :quirks_mode => false))
727
+ assert_empty(Oj.load('{}', :quirks_mode => true))
695
728
  end
696
729
 
697
730
  def test_omit_nil
@@ -707,6 +740,52 @@ class Juice < Minitest::Test
707
740
  assert_equal(%|{"x":{"a":1}}|, json)
708
741
  end
709
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
+
710
789
  def dump_and_load(obj, trace=false)
711
790
  json = Oj.dump(obj, :indent => 2)
712
791
  puts json if trace