oj 3.11.5 → 3.16.5

Sign up to get free protection for your applications and to get access to all the features.
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