json 1.8.3 → 2.5.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.
- checksums.yaml +5 -5
- data/{CHANGES → CHANGES.md} +241 -90
- data/Gemfile +10 -6
- data/{COPYING-json-jruby → LICENSE} +5 -6
- data/{README.rdoc → README.md} +201 -134
- data/VERSION +1 -1
- data/ext/json/ext/fbuffer/fbuffer.h +0 -3
- data/ext/json/ext/generator/generator.c +264 -104
- data/ext/json/ext/generator/generator.h +12 -4
- data/ext/json/ext/parser/extconf.rb +28 -0
- data/ext/json/ext/parser/parser.c +425 -462
- data/ext/json/ext/parser/parser.h +5 -5
- data/ext/json/ext/parser/parser.rl +181 -181
- data/ext/json/extconf.rb +1 -1
- data/json.gemspec +0 -0
- data/lib/json.rb +550 -29
- data/lib/json/add/bigdecimal.rb +3 -2
- data/lib/json/add/complex.rb +4 -4
- data/lib/json/add/core.rb +1 -0
- data/lib/json/add/date.rb +1 -1
- data/lib/json/add/date_time.rb +1 -1
- data/lib/json/add/exception.rb +1 -1
- data/lib/json/add/ostruct.rb +3 -3
- data/lib/json/add/range.rb +1 -1
- data/lib/json/add/rational.rb +3 -3
- data/lib/json/add/regexp.rb +3 -3
- data/lib/json/add/set.rb +29 -0
- data/lib/json/add/struct.rb +1 -1
- data/lib/json/add/symbol.rb +1 -1
- data/lib/json/add/time.rb +1 -1
- data/lib/json/common.rb +381 -162
- data/lib/json/ext.rb +0 -6
- data/lib/json/generic_object.rb +5 -4
- data/lib/json/pure.rb +2 -8
- data/lib/json/pure/generator.rb +83 -126
- data/lib/json/pure/parser.rb +62 -84
- data/lib/json/version.rb +2 -1
- data/tests/fixtures/fail29.json +1 -0
- data/tests/fixtures/fail30.json +1 -0
- data/tests/fixtures/fail31.json +1 -0
- data/tests/fixtures/fail32.json +1 -0
- data/tests/fixtures/obsolete_fail1.json +1 -0
- data/tests/{test_json_addition.rb → json_addition_test.rb} +28 -25
- data/tests/json_common_interface_test.rb +169 -0
- data/tests/json_encoding_test.rb +107 -0
- data/tests/json_ext_parser_test.rb +15 -0
- data/tests/{test_json_fixtures.rb → json_fixtures_test.rb} +13 -8
- data/tests/{test_json_generate.rb → json_generator_test.rb} +109 -47
- data/tests/{test_json_generic_object.rb → json_generic_object_test.rb} +15 -8
- data/tests/json_parser_test.rb +497 -0
- data/tests/json_string_matching_test.rb +38 -0
- data/tests/lib/core_assertions.rb +763 -0
- data/tests/lib/envutil.rb +365 -0
- data/tests/lib/find_executable.rb +22 -0
- data/tests/lib/helper.rb +4 -0
- data/tests/ractor_test.rb +30 -0
- data/tests/test_helper.rb +17 -0
- metadata +48 -76
- data/.gitignore +0 -16
- data/.travis.yml +0 -26
- data/COPYING +0 -58
- data/GPL +0 -340
- data/README-json-jruby.markdown +0 -33
- data/Rakefile +0 -412
- data/TODO +0 -1
- data/data/example.json +0 -1
- data/data/index.html +0 -38
- data/data/prototype.js +0 -4184
- data/diagrams/.keep +0 -0
- data/install.rb +0 -23
- data/java/src/json/ext/ByteListTranscoder.java +0 -167
- data/java/src/json/ext/Generator.java +0 -444
- data/java/src/json/ext/GeneratorMethods.java +0 -232
- data/java/src/json/ext/GeneratorService.java +0 -43
- data/java/src/json/ext/GeneratorState.java +0 -543
- data/java/src/json/ext/OptionsReader.java +0 -114
- data/java/src/json/ext/Parser.java +0 -2645
- data/java/src/json/ext/Parser.rl +0 -969
- data/java/src/json/ext/ParserService.java +0 -35
- data/java/src/json/ext/RuntimeInfo.java +0 -121
- data/java/src/json/ext/StringDecoder.java +0 -167
- data/java/src/json/ext/StringEncoder.java +0 -106
- data/java/src/json/ext/Utils.java +0 -89
- data/json-java.gemspec +0 -23
- data/json_pure.gemspec +0 -40
- data/tests/fixtures/fail1.json +0 -1
- data/tests/setup_variant.rb +0 -11
- data/tests/test_json.rb +0 -553
- data/tests/test_json_encoding.rb +0 -65
- data/tests/test_json_string_matching.rb +0 -39
- data/tests/test_json_unicode.rb +0 -72
- data/tools/fuzz.rb +0 -139
- data/tools/server.rb +0 -62
@@ -1,18 +1,16 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#frozen_string_literal: false
|
2
|
+
require 'test_helper'
|
3
3
|
|
4
|
-
|
5
|
-
require File.join(File.dirname(__FILE__), 'setup_variant')
|
6
|
-
|
7
|
-
class TestJSONFixtures < Test::Unit::TestCase
|
4
|
+
class JSONFixturesTest < Test::Unit::TestCase
|
8
5
|
def setup
|
9
|
-
fixtures = File.join(File.dirname(__FILE__), 'fixtures
|
6
|
+
fixtures = File.join(File.dirname(__FILE__), 'fixtures/{fail,pass}*.json')
|
10
7
|
passed, failed = Dir[fixtures].partition { |f| f['pass'] }
|
11
8
|
@passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
|
12
9
|
@failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
|
13
10
|
end
|
14
11
|
|
15
12
|
def test_passing
|
13
|
+
verbose_bak, $VERBOSE = $VERBOSE, nil
|
16
14
|
for name, source in @passed
|
17
15
|
begin
|
18
16
|
assert JSON.parse(source),
|
@@ -22,14 +20,21 @@ class TestJSONFixtures < Test::Unit::TestCase
|
|
22
20
|
raise e
|
23
21
|
end
|
24
22
|
end
|
23
|
+
ensure
|
24
|
+
$VERBOSE = verbose_bak
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_failing
|
28
28
|
for name, source in @failed
|
29
|
-
|
29
|
+
assert_raise(JSON::ParserError, JSON::NestingError,
|
30
30
|
"Did not fail for fixture '#{name}': #{source.inspect}") do
|
31
31
|
JSON.parse(source)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
def test_sanity
|
37
|
+
assert(@passed.size > 5)
|
38
|
+
assert(@failed.size > 20)
|
39
|
+
end
|
35
40
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
|
+
# frozen_string_literal: false
|
3
4
|
|
4
|
-
require '
|
5
|
-
require File.join(File.dirname(__FILE__), 'setup_variant')
|
5
|
+
require 'test_helper'
|
6
6
|
|
7
|
-
class
|
7
|
+
class JSONGeneratorTest < Test::Unit::TestCase
|
8
8
|
include JSON
|
9
9
|
|
10
10
|
def setup
|
@@ -40,25 +40,38 @@ class TestJSONGenerate < Test::Unit::TestCase
|
|
40
40
|
EOT
|
41
41
|
end
|
42
42
|
|
43
|
+
def silence
|
44
|
+
v = $VERBOSE
|
45
|
+
$VERBOSE = nil
|
46
|
+
yield
|
47
|
+
ensure
|
48
|
+
$VERBOSE = v
|
49
|
+
end
|
50
|
+
|
43
51
|
def test_generate
|
44
52
|
json = generate(@hash)
|
45
|
-
assert_equal(
|
53
|
+
assert_equal(parse(@json2), parse(json))
|
46
54
|
json = JSON[@hash]
|
47
|
-
assert_equal(
|
55
|
+
assert_equal(parse(@json2), parse(json))
|
48
56
|
parsed_json = parse(json)
|
49
57
|
assert_equal(@hash, parsed_json)
|
50
58
|
json = generate({1=>2})
|
51
59
|
assert_equal('{"1":2}', json)
|
52
60
|
parsed_json = parse(json)
|
53
61
|
assert_equal({"1"=>2}, parsed_json)
|
54
|
-
|
55
|
-
assert_equal '666', generate(666, :quirks_mode => true)
|
62
|
+
assert_equal '666', generate(666)
|
56
63
|
end
|
57
64
|
|
58
65
|
def test_generate_pretty
|
66
|
+
json = pretty_generate({})
|
67
|
+
assert_equal(<<'EOT'.chomp, json)
|
68
|
+
{
|
69
|
+
}
|
70
|
+
EOT
|
59
71
|
json = pretty_generate(@hash)
|
60
|
-
# hashes aren't (insertion) ordered on every ruby implementation
|
61
|
-
assert_equal(
|
72
|
+
# hashes aren't (insertion) ordered on every ruby implementation
|
73
|
+
# assert_equal(@json3, json)
|
74
|
+
assert_equal(parse(@json3), parse(json))
|
62
75
|
parsed_json = parse(json)
|
63
76
|
assert_equal(@hash, parsed_json)
|
64
77
|
json = pretty_generate({1=>2})
|
@@ -69,8 +82,7 @@ EOT
|
|
69
82
|
EOT
|
70
83
|
parsed_json = parse(json)
|
71
84
|
assert_equal({"1"=>2}, parsed_json)
|
72
|
-
|
73
|
-
assert_equal '666', pretty_generate(666, :quirks_mode => true)
|
85
|
+
assert_equal '666', pretty_generate(666)
|
74
86
|
end
|
75
87
|
|
76
88
|
def test_generate_custom
|
@@ -88,30 +100,26 @@ EOT
|
|
88
100
|
|
89
101
|
def test_fast_generate
|
90
102
|
json = fast_generate(@hash)
|
91
|
-
assert_equal(
|
103
|
+
assert_equal(parse(@json2), parse(json))
|
92
104
|
parsed_json = parse(json)
|
93
105
|
assert_equal(@hash, parsed_json)
|
94
106
|
json = fast_generate({1=>2})
|
95
107
|
assert_equal('{"1":2}', json)
|
96
108
|
parsed_json = parse(json)
|
97
109
|
assert_equal({"1"=>2}, parsed_json)
|
98
|
-
|
99
|
-
assert_equal '666', fast_generate(666, :quirks_mode => true)
|
110
|
+
assert_equal '666', fast_generate(666)
|
100
111
|
end
|
101
112
|
|
102
113
|
def test_own_state
|
103
114
|
state = State.new
|
104
115
|
json = generate(@hash, state)
|
105
|
-
assert_equal(
|
116
|
+
assert_equal(parse(@json2), parse(json))
|
106
117
|
parsed_json = parse(json)
|
107
118
|
assert_equal(@hash, parsed_json)
|
108
119
|
json = generate({1=>2}, state)
|
109
120
|
assert_equal('{"1":2}', json)
|
110
121
|
parsed_json = parse(json)
|
111
122
|
assert_equal({"1"=>2}, parsed_json)
|
112
|
-
assert_raise(GeneratorError) { generate(666, state) }
|
113
|
-
state.quirks_mode = true
|
114
|
-
assert state.quirks_mode?
|
115
123
|
assert_equal '666', generate(666, state)
|
116
124
|
end
|
117
125
|
|
@@ -123,25 +131,25 @@ EOT
|
|
123
131
|
assert s[:check_circular?]
|
124
132
|
h = { 1=>2 }
|
125
133
|
h[3] = h
|
126
|
-
|
127
|
-
|
134
|
+
assert_raise(JSON::NestingError) { generate(h) }
|
135
|
+
assert_raise(JSON::NestingError) { generate(h, s) }
|
128
136
|
s = JSON.state.new
|
129
137
|
a = [ 1, 2 ]
|
130
138
|
a << a
|
131
|
-
|
139
|
+
assert_raise(JSON::NestingError) { generate(a, s) }
|
132
140
|
assert s.check_circular?
|
133
141
|
assert s[:check_circular?]
|
134
142
|
end
|
135
143
|
|
136
144
|
def test_pretty_state
|
137
|
-
state =
|
145
|
+
state = JSON.create_pretty_state
|
138
146
|
assert_equal({
|
139
147
|
:allow_nan => false,
|
140
148
|
:array_nl => "\n",
|
141
149
|
:ascii_only => false,
|
142
150
|
:buffer_initial_length => 1024,
|
143
|
-
:quirks_mode => false,
|
144
151
|
:depth => 0,
|
152
|
+
:escape_slash => false,
|
145
153
|
:indent => " ",
|
146
154
|
:max_nesting => 100,
|
147
155
|
:object_nl => "\n",
|
@@ -151,14 +159,14 @@ EOT
|
|
151
159
|
end
|
152
160
|
|
153
161
|
def test_safe_state
|
154
|
-
state =
|
162
|
+
state = JSON::State.new
|
155
163
|
assert_equal({
|
156
164
|
:allow_nan => false,
|
157
165
|
:array_nl => "",
|
158
166
|
:ascii_only => false,
|
159
167
|
:buffer_initial_length => 1024,
|
160
|
-
:quirks_mode => false,
|
161
168
|
:depth => 0,
|
169
|
+
:escape_slash => false,
|
162
170
|
:indent => "",
|
163
171
|
:max_nesting => 100,
|
164
172
|
:object_nl => "",
|
@@ -168,14 +176,14 @@ EOT
|
|
168
176
|
end
|
169
177
|
|
170
178
|
def test_fast_state
|
171
|
-
state =
|
179
|
+
state = JSON.create_fast_state
|
172
180
|
assert_equal({
|
173
181
|
:allow_nan => false,
|
174
182
|
:array_nl => "",
|
175
183
|
:ascii_only => false,
|
176
184
|
:buffer_initial_length => 1024,
|
177
|
-
:quirks_mode => false,
|
178
185
|
:depth => 0,
|
186
|
+
:escape_slash => false,
|
179
187
|
:indent => "",
|
180
188
|
:max_nesting => 0,
|
181
189
|
:object_nl => "",
|
@@ -185,34 +193,30 @@ EOT
|
|
185
193
|
end
|
186
194
|
|
187
195
|
def test_allow_nan
|
188
|
-
|
196
|
+
assert_raise(GeneratorError) { generate([JSON::NaN]) }
|
189
197
|
assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
|
190
|
-
|
191
|
-
|
198
|
+
assert_raise(GeneratorError) { fast_generate([JSON::NaN]) }
|
199
|
+
assert_raise(GeneratorError) { pretty_generate([JSON::NaN]) }
|
192
200
|
assert_equal "[\n NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
|
193
|
-
|
201
|
+
assert_raise(GeneratorError) { generate([JSON::Infinity]) }
|
194
202
|
assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
|
195
|
-
|
196
|
-
|
203
|
+
assert_raise(GeneratorError) { fast_generate([JSON::Infinity]) }
|
204
|
+
assert_raise(GeneratorError) { pretty_generate([JSON::Infinity]) }
|
197
205
|
assert_equal "[\n Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
|
198
|
-
|
206
|
+
assert_raise(GeneratorError) { generate([JSON::MinusInfinity]) }
|
199
207
|
assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
|
200
|
-
|
201
|
-
|
208
|
+
assert_raise(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
|
209
|
+
assert_raise(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
|
202
210
|
assert_equal "[\n -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
|
203
211
|
end
|
204
212
|
|
205
213
|
def test_depth
|
206
214
|
ary = []; ary << ary
|
207
|
-
|
208
|
-
|
209
|
-
assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
|
210
|
-
assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
|
211
|
-
assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) }
|
212
|
-
assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
|
215
|
+
assert_raise(JSON::NestingError) { generate(ary) }
|
216
|
+
assert_raise(JSON::NestingError) { JSON.pretty_generate(ary) }
|
213
217
|
s = JSON.state.new
|
214
218
|
assert_equal 0, s.depth
|
215
|
-
|
219
|
+
assert_raise(JSON::NestingError) { ary.to_json(s) }
|
216
220
|
assert_equal 100, s.depth
|
217
221
|
end
|
218
222
|
|
@@ -228,7 +232,7 @@ EOT
|
|
228
232
|
end
|
229
233
|
|
230
234
|
def test_gc
|
231
|
-
if respond_to?(:assert_in_out_err)
|
235
|
+
if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/)
|
232
236
|
assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
|
233
237
|
bignum_too_long_to_embed_as_string = 1234567890123456789012345
|
234
238
|
expect = bignum_too_long_to_embed_as_string.to_s
|
@@ -285,12 +289,13 @@ EOT
|
|
285
289
|
if defined?(JSON::Ext::Generator)
|
286
290
|
def test_broken_bignum # [ruby-core:38867]
|
287
291
|
pid = fork do
|
288
|
-
|
292
|
+
x = 1 << 64
|
293
|
+
x.class.class_eval do
|
289
294
|
def to_s
|
290
295
|
end
|
291
296
|
end
|
292
297
|
begin
|
293
|
-
JSON::Ext::Generator::State.new.generate(
|
298
|
+
JSON::Ext::Generator::State.new.generate(x)
|
294
299
|
exit 1
|
295
300
|
rescue TypeError
|
296
301
|
exit 0
|
@@ -331,7 +336,64 @@ EOT
|
|
331
336
|
|
332
337
|
def test_json_generate
|
333
338
|
assert_raise JSON::GeneratorError do
|
334
|
-
assert_equal true,
|
339
|
+
assert_equal true, generate(["\xea"])
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_nesting
|
344
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
345
|
+
too_deep_ary = eval too_deep
|
346
|
+
assert_raise(JSON::NestingError) { generate too_deep_ary }
|
347
|
+
assert_raise(JSON::NestingError) { generate too_deep_ary, :max_nesting => 100 }
|
348
|
+
ok = generate too_deep_ary, :max_nesting => 101
|
349
|
+
assert_equal too_deep, ok
|
350
|
+
ok = generate too_deep_ary, :max_nesting => nil
|
351
|
+
assert_equal too_deep, ok
|
352
|
+
ok = generate too_deep_ary, :max_nesting => false
|
353
|
+
assert_equal too_deep, ok
|
354
|
+
ok = generate too_deep_ary, :max_nesting => 0
|
355
|
+
assert_equal too_deep, ok
|
356
|
+
end
|
357
|
+
|
358
|
+
def test_backslash
|
359
|
+
data = [ '\\.(?i:gif|jpe?g|png)$' ]
|
360
|
+
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
361
|
+
assert_equal json, generate(data)
|
362
|
+
#
|
363
|
+
data = [ '\\"' ]
|
364
|
+
json = '["\\\\\""]'
|
365
|
+
assert_equal json, generate(data)
|
366
|
+
#
|
367
|
+
data = [ '/' ]
|
368
|
+
json = '["/"]'
|
369
|
+
assert_equal json, generate(data)
|
370
|
+
#
|
371
|
+
data = [ '/' ]
|
372
|
+
json = '["\/"]'
|
373
|
+
assert_equal json, generate(data, :escape_slash => true)
|
374
|
+
#
|
375
|
+
data = ['"']
|
376
|
+
json = '["\""]'
|
377
|
+
assert_equal json, generate(data)
|
378
|
+
#
|
379
|
+
data = ["'"]
|
380
|
+
json = '["\\\'"]'
|
381
|
+
assert_equal '["\'"]', generate(data)
|
382
|
+
end
|
383
|
+
|
384
|
+
def test_string_subclass
|
385
|
+
s = Class.new(String) do
|
386
|
+
def to_s; self; end
|
387
|
+
undef to_json
|
388
|
+
end
|
389
|
+
assert_nothing_raised(SystemStackError) do
|
390
|
+
assert_equal '["foo"]', JSON.generate([s.new('foo')])
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
if defined?(Encoding)
|
395
|
+
def test_nonutf8_encoding
|
396
|
+
assert_equal("\"5\u{b0}\"", "5\xb0".force_encoding("iso-8859-1").to_json)
|
335
397
|
end
|
336
398
|
end
|
337
399
|
end
|
@@ -1,9 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
#frozen_string_literal: false
|
2
|
+
require 'test_helper'
|
3
3
|
|
4
|
-
|
5
|
-
require File.join(File.dirname(__FILE__), 'setup_variant')
|
6
|
-
class TestJSONGenericObject < Test::Unit::TestCase
|
4
|
+
class JSONGenericObjectTest < Test::Unit::TestCase
|
7
5
|
include JSON
|
8
6
|
|
9
7
|
def setup
|
@@ -26,11 +24,20 @@ class TestJSONGenericObject < Test::Unit::TestCase
|
|
26
24
|
end
|
27
25
|
|
28
26
|
def test_parse_json
|
29
|
-
assert_kind_of Hash,
|
27
|
+
assert_kind_of Hash,
|
28
|
+
JSON(
|
29
|
+
'{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }',
|
30
|
+
:create_additions => true
|
31
|
+
)
|
30
32
|
switch_json_creatable do
|
31
|
-
assert_equal @go, l =
|
33
|
+
assert_equal @go, l =
|
34
|
+
JSON(
|
35
|
+
'{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }',
|
36
|
+
:create_additions => true
|
37
|
+
)
|
32
38
|
assert_equal 1, l.a
|
33
|
-
assert_equal @go,
|
39
|
+
assert_equal @go,
|
40
|
+
l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject)
|
34
41
|
assert_equal 1, l.a
|
35
42
|
assert_equal GenericObject[:a => GenericObject[:b => 2]],
|
36
43
|
l = JSON('{ "a": { "b": 2 } }', :object_class => GenericObject)
|
@@ -0,0 +1,497 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: false
|
3
|
+
require 'test_helper'
|
4
|
+
require 'stringio'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'bigdecimal'
|
8
|
+
|
9
|
+
class JSONParserTest < Test::Unit::TestCase
|
10
|
+
include JSON
|
11
|
+
|
12
|
+
def test_construction
|
13
|
+
parser = JSON::Parser.new('test')
|
14
|
+
assert_equal 'test', parser.source
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_argument_encoding
|
18
|
+
source = "{}".encode("UTF-16")
|
19
|
+
JSON::Parser.new(source)
|
20
|
+
assert_equal Encoding::UTF_16, source.encoding
|
21
|
+
end if defined?(Encoding::UTF_16)
|
22
|
+
|
23
|
+
def test_error_message_encoding
|
24
|
+
bug10705 = '[ruby-core:67386] [Bug #10705]'
|
25
|
+
json = ".\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
|
26
|
+
e = assert_raise(JSON::ParserError) {
|
27
|
+
JSON::Ext::Parser.new(json).parse
|
28
|
+
}
|
29
|
+
assert_equal(Encoding::UTF_8, e.message.encoding, bug10705)
|
30
|
+
assert_include(e.message, json, bug10705)
|
31
|
+
end if defined?(Encoding::UTF_8) and defined?(JSON::Ext::Parser)
|
32
|
+
|
33
|
+
def test_parsing
|
34
|
+
parser = JSON::Parser.new('"test"')
|
35
|
+
assert_equal 'test', parser.parse
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_parser_reset
|
39
|
+
parser = Parser.new('{"a":"b"}')
|
40
|
+
assert_equal({ 'a' => 'b' }, parser.parse)
|
41
|
+
assert_equal({ 'a' => 'b' }, parser.parse)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_parse_values
|
45
|
+
assert_equal(nil, parse('null'))
|
46
|
+
assert_equal(false, parse('false'))
|
47
|
+
assert_equal(true, parse('true'))
|
48
|
+
assert_equal(-23, parse('-23'))
|
49
|
+
assert_equal(23, parse('23'))
|
50
|
+
assert_in_delta(0.23, parse('0.23'), 1e-2)
|
51
|
+
assert_in_delta(0.0, parse('0e0'), 1e-2)
|
52
|
+
assert_equal("", parse('""'))
|
53
|
+
assert_equal("foobar", parse('"foobar"'))
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_parse_simple_arrays
|
57
|
+
assert_equal([], parse('[]'))
|
58
|
+
assert_equal([], parse(' [ ] '))
|
59
|
+
assert_equal([ nil ], parse('[null]'))
|
60
|
+
assert_equal([ false ], parse('[false]'))
|
61
|
+
assert_equal([ true ], parse('[true]'))
|
62
|
+
assert_equal([ -23 ], parse('[-23]'))
|
63
|
+
assert_equal([ 23 ], parse('[23]'))
|
64
|
+
assert_equal_float([ 0.23 ], parse('[0.23]'))
|
65
|
+
assert_equal_float([ 0.0 ], parse('[0e0]'))
|
66
|
+
assert_equal([""], parse('[""]'))
|
67
|
+
assert_equal(["foobar"], parse('["foobar"]'))
|
68
|
+
assert_equal([{}], parse('[{}]'))
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_parse_simple_objects
|
72
|
+
assert_equal({}, parse('{}'))
|
73
|
+
assert_equal({}, parse(' { } '))
|
74
|
+
assert_equal({ "a" => nil }, parse('{ "a" : null}'))
|
75
|
+
assert_equal({ "a" => nil }, parse('{"a":null}'))
|
76
|
+
assert_equal({ "a" => false }, parse('{ "a" : false } '))
|
77
|
+
assert_equal({ "a" => false }, parse('{"a":false}'))
|
78
|
+
assert_raise(JSON::ParserError) { parse('{false}') }
|
79
|
+
assert_equal({ "a" => true }, parse('{"a":true}'))
|
80
|
+
assert_equal({ "a" => true }, parse(' { "a" : true } '))
|
81
|
+
assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
|
82
|
+
assert_equal({ "a" => -23 }, parse(' { "a" : -23 } '))
|
83
|
+
assert_equal({ "a" => 23 }, parse('{"a":23 } '))
|
84
|
+
assert_equal({ "a" => 23 }, parse(' { "a" : 23 } '))
|
85
|
+
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
|
86
|
+
assert_equal({ "a" => 0.23 }, parse(' { "a" : 0.23 } '))
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_parse_numbers
|
90
|
+
assert_raise(JSON::ParserError) { parse('+23.2') }
|
91
|
+
assert_raise(JSON::ParserError) { parse('+23') }
|
92
|
+
assert_raise(JSON::ParserError) { parse('.23') }
|
93
|
+
assert_raise(JSON::ParserError) { parse('023') }
|
94
|
+
assert_equal(23, parse('23'))
|
95
|
+
assert_equal(-23, parse('-23'))
|
96
|
+
assert_equal_float(3.141, parse('3.141'))
|
97
|
+
assert_equal_float(-3.141, parse('-3.141'))
|
98
|
+
assert_equal_float(3.141, parse('3141e-3'))
|
99
|
+
assert_equal_float(3.141, parse('3141.1e-3'))
|
100
|
+
assert_equal_float(3.141, parse('3141E-3'))
|
101
|
+
assert_equal_float(3.141, parse('3141.0E-3'))
|
102
|
+
assert_equal_float(-3.141, parse('-3141.0e-3'))
|
103
|
+
assert_equal_float(-3.141, parse('-3141e-3'))
|
104
|
+
assert_raise(ParserError) { parse('NaN') }
|
105
|
+
assert parse('NaN', :allow_nan => true).nan?
|
106
|
+
assert_raise(ParserError) { parse('Infinity') }
|
107
|
+
assert_equal(1.0/0, parse('Infinity', :allow_nan => true))
|
108
|
+
assert_raise(ParserError) { parse('-Infinity') }
|
109
|
+
assert_equal(-1.0/0, parse('-Infinity', :allow_nan => true))
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_parse_bigdecimals
|
113
|
+
assert_equal(BigDecimal, JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"].class)
|
114
|
+
assert_equal(BigDecimal("0.901234567890123456789E1"),JSON.parse('{"foo": 9.01234567890123456789}', decimal_class: BigDecimal)["foo"] )
|
115
|
+
end
|
116
|
+
|
117
|
+
if Array.method_defined?(:permutation)
|
118
|
+
def test_parse_more_complex_arrays
|
119
|
+
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
120
|
+
a.permutation.each do |perm|
|
121
|
+
json = pretty_generate(perm)
|
122
|
+
assert_equal perm, parse(json)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_parse_complex_objects
|
127
|
+
a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
|
128
|
+
a.permutation.each do |perm|
|
129
|
+
s = "a"
|
130
|
+
orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
|
131
|
+
json = pretty_generate(orig_obj)
|
132
|
+
assert_equal orig_obj, parse(json)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_parse_arrays
|
138
|
+
assert_equal([1,2,3], parse('[1,2,3]'))
|
139
|
+
assert_equal([1.2,2,3], parse('[1.2,2,3]'))
|
140
|
+
assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
|
141
|
+
assert_equal([], parse('[]'))
|
142
|
+
assert_equal([], parse(' [ ] '))
|
143
|
+
assert_equal([1], parse('[1]'))
|
144
|
+
assert_equal([1], parse(' [ 1 ] '))
|
145
|
+
ary = [[1], ["foo"], [3.14], [4711.0], [2.718], [nil],
|
146
|
+
[[1, -2, 3]], [false], [true]]
|
147
|
+
assert_equal(ary,
|
148
|
+
parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]],[false],[true]]'))
|
149
|
+
assert_equal(ary, parse(%Q{ [ [1] , ["foo"] , [3.14] \t , [47.11e+2]\s
|
150
|
+
, [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ] }))
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_parse_json_primitive_values
|
154
|
+
assert_raise(JSON::ParserError) { parse('') }
|
155
|
+
assert_raise(TypeError) { parse(nil) }
|
156
|
+
assert_raise(JSON::ParserError) { parse(' /* foo */ ') }
|
157
|
+
assert_equal nil, parse('null')
|
158
|
+
assert_equal false, parse('false')
|
159
|
+
assert_equal true, parse('true')
|
160
|
+
assert_equal 23, parse('23')
|
161
|
+
assert_equal 1, parse('1')
|
162
|
+
assert_equal_float 3.141, parse('3.141'), 1E-3
|
163
|
+
assert_equal 2 ** 64, parse('18446744073709551616')
|
164
|
+
assert_equal 'foo', parse('"foo"')
|
165
|
+
assert parse('NaN', :allow_nan => true).nan?
|
166
|
+
assert parse('Infinity', :allow_nan => true).infinite?
|
167
|
+
assert parse('-Infinity', :allow_nan => true).infinite?
|
168
|
+
assert_raise(JSON::ParserError) { parse('[ 1, ]') }
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_parse_some_strings
|
172
|
+
assert_equal([""], parse('[""]'))
|
173
|
+
assert_equal(["\\"], parse('["\\\\"]'))
|
174
|
+
assert_equal(['"'], parse('["\""]'))
|
175
|
+
assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
|
176
|
+
assert_equal(
|
177
|
+
["\"\b\n\r\t\0\037"],
|
178
|
+
parse('["\"\b\n\r\t\u0000\u001f"]')
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_parse_big_integers
|
183
|
+
json1 = JSON(orig = (1 << 31) - 1)
|
184
|
+
assert_equal orig, parse(json1)
|
185
|
+
json2 = JSON(orig = 1 << 31)
|
186
|
+
assert_equal orig, parse(json2)
|
187
|
+
json3 = JSON(orig = (1 << 62) - 1)
|
188
|
+
assert_equal orig, parse(json3)
|
189
|
+
json4 = JSON(orig = 1 << 62)
|
190
|
+
assert_equal orig, parse(json4)
|
191
|
+
json5 = JSON(orig = 1 << 64)
|
192
|
+
assert_equal orig, parse(json5)
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_some_wrong_inputs
|
196
|
+
assert_raise(ParserError) { parse('[] bla') }
|
197
|
+
assert_raise(ParserError) { parse('[] 1') }
|
198
|
+
assert_raise(ParserError) { parse('[] []') }
|
199
|
+
assert_raise(ParserError) { parse('[] {}') }
|
200
|
+
assert_raise(ParserError) { parse('{} []') }
|
201
|
+
assert_raise(ParserError) { parse('{} {}') }
|
202
|
+
assert_raise(ParserError) { parse('[NULL]') }
|
203
|
+
assert_raise(ParserError) { parse('[FALSE]') }
|
204
|
+
assert_raise(ParserError) { parse('[TRUE]') }
|
205
|
+
assert_raise(ParserError) { parse('[07] ') }
|
206
|
+
assert_raise(ParserError) { parse('[0a]') }
|
207
|
+
assert_raise(ParserError) { parse('[1.]') }
|
208
|
+
assert_raise(ParserError) { parse(' ') }
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_symbolize_names
|
212
|
+
assert_equal({ "foo" => "bar", "baz" => "quux" },
|
213
|
+
parse('{"foo":"bar", "baz":"quux"}'))
|
214
|
+
assert_equal({ :foo => "bar", :baz => "quux" },
|
215
|
+
parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
|
216
|
+
assert_raise(ArgumentError) do
|
217
|
+
parse('{}', :symbolize_names => true, :create_additions => true)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_freeze
|
222
|
+
assert_predicate parse('{}', :freeze => true), :frozen?
|
223
|
+
assert_predicate parse('[]', :freeze => true), :frozen?
|
224
|
+
assert_predicate parse('"foo"', :freeze => true), :frozen?
|
225
|
+
|
226
|
+
if string_deduplication_available?
|
227
|
+
assert_same(-'foo', parse('"foo"', :freeze => true))
|
228
|
+
assert_same(-'foo', parse('{"foo": 1}', :freeze => true).keys.first)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_parse_comments
|
233
|
+
json = <<EOT
|
234
|
+
{
|
235
|
+
"key1":"value1", // eol comment
|
236
|
+
"key2":"value2" /* multi line
|
237
|
+
* comment */,
|
238
|
+
"key3":"value3" /* multi line
|
239
|
+
// nested eol comment
|
240
|
+
* comment */
|
241
|
+
}
|
242
|
+
EOT
|
243
|
+
assert_equal(
|
244
|
+
{ "key1" => "value1", "key2" => "value2", "key3" => "value3" },
|
245
|
+
parse(json))
|
246
|
+
json = <<EOT
|
247
|
+
{
|
248
|
+
"key1":"value1" /* multi line
|
249
|
+
// nested eol comment
|
250
|
+
/* illegal nested multi line comment */
|
251
|
+
* comment */
|
252
|
+
}
|
253
|
+
EOT
|
254
|
+
assert_raise(ParserError) { parse(json) }
|
255
|
+
json = <<EOT
|
256
|
+
{
|
257
|
+
"key1":"value1" /* multi line
|
258
|
+
// nested eol comment
|
259
|
+
closed multi comment */
|
260
|
+
and again, throw an Error */
|
261
|
+
}
|
262
|
+
EOT
|
263
|
+
assert_raise(ParserError) { parse(json) }
|
264
|
+
json = <<EOT
|
265
|
+
{
|
266
|
+
"key1":"value1" /*/*/
|
267
|
+
}
|
268
|
+
EOT
|
269
|
+
assert_equal({ "key1" => "value1" }, parse(json))
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_nesting
|
273
|
+
too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
|
274
|
+
too_deep_ary = eval too_deep
|
275
|
+
assert_raise(JSON::NestingError) { parse too_deep }
|
276
|
+
assert_raise(JSON::NestingError) { parse too_deep, :max_nesting => 100 }
|
277
|
+
ok = parse too_deep, :max_nesting => 101
|
278
|
+
assert_equal too_deep_ary, ok
|
279
|
+
ok = parse too_deep, :max_nesting => nil
|
280
|
+
assert_equal too_deep_ary, ok
|
281
|
+
ok = parse too_deep, :max_nesting => false
|
282
|
+
assert_equal too_deep_ary, ok
|
283
|
+
ok = parse too_deep, :max_nesting => 0
|
284
|
+
assert_equal too_deep_ary, ok
|
285
|
+
end
|
286
|
+
|
287
|
+
def test_backslash
|
288
|
+
data = [ '\\.(?i:gif|jpe?g|png)$' ]
|
289
|
+
json = '["\\\\.(?i:gif|jpe?g|png)$"]'
|
290
|
+
assert_equal data, parse(json)
|
291
|
+
#
|
292
|
+
data = [ '\\"' ]
|
293
|
+
json = '["\\\\\""]'
|
294
|
+
assert_equal data, parse(json)
|
295
|
+
#
|
296
|
+
json = '["/"]'
|
297
|
+
data = [ '/' ]
|
298
|
+
assert_equal data, parse(json)
|
299
|
+
#
|
300
|
+
json = '["\""]'
|
301
|
+
data = ['"']
|
302
|
+
assert_equal data, parse(json)
|
303
|
+
#
|
304
|
+
json = '["\\\'"]'
|
305
|
+
data = ["'"]
|
306
|
+
assert_equal data, parse(json)
|
307
|
+
|
308
|
+
json = '["\/"]'
|
309
|
+
data = [ '/' ]
|
310
|
+
assert_equal data, parse(json)
|
311
|
+
end
|
312
|
+
|
313
|
+
class SubArray < Array
|
314
|
+
def <<(v)
|
315
|
+
@shifted = true
|
316
|
+
super
|
317
|
+
end
|
318
|
+
|
319
|
+
def shifted?
|
320
|
+
@shifted
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
class SubArray2 < Array
|
325
|
+
def to_json(*a)
|
326
|
+
{
|
327
|
+
JSON.create_id => self.class.name,
|
328
|
+
'ary' => to_a,
|
329
|
+
}.to_json(*a)
|
330
|
+
end
|
331
|
+
|
332
|
+
def self.json_create(o)
|
333
|
+
o.delete JSON.create_id
|
334
|
+
o['ary']
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
class SubArrayWrapper
|
339
|
+
def initialize
|
340
|
+
@data = []
|
341
|
+
end
|
342
|
+
|
343
|
+
attr_reader :data
|
344
|
+
|
345
|
+
def [](index)
|
346
|
+
@data[index]
|
347
|
+
end
|
348
|
+
|
349
|
+
def <<(value)
|
350
|
+
@data << value
|
351
|
+
@shifted = true
|
352
|
+
end
|
353
|
+
|
354
|
+
def shifted?
|
355
|
+
@shifted
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_parse_array_custom_array_derived_class
|
360
|
+
res = parse('[1,2]', :array_class => SubArray)
|
361
|
+
assert_equal([1,2], res)
|
362
|
+
assert_equal(SubArray, res.class)
|
363
|
+
assert res.shifted?
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_parse_array_custom_non_array_derived_class
|
367
|
+
res = parse('[1,2]', :array_class => SubArrayWrapper)
|
368
|
+
assert_equal([1,2], res.data)
|
369
|
+
assert_equal(SubArrayWrapper, res.class)
|
370
|
+
assert res.shifted?
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_parse_object
|
374
|
+
assert_equal({}, parse('{}'))
|
375
|
+
assert_equal({}, parse(' { } '))
|
376
|
+
assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
|
377
|
+
assert_equal({'foo'=>'bar'}, parse(' { "foo" : "bar" } '))
|
378
|
+
end
|
379
|
+
|
380
|
+
class SubHash < Hash
|
381
|
+
def []=(k, v)
|
382
|
+
@item_set = true
|
383
|
+
super
|
384
|
+
end
|
385
|
+
|
386
|
+
def item_set?
|
387
|
+
@item_set
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
class SubHash2 < Hash
|
392
|
+
def to_json(*a)
|
393
|
+
{
|
394
|
+
JSON.create_id => self.class.name,
|
395
|
+
}.merge(self).to_json(*a)
|
396
|
+
end
|
397
|
+
|
398
|
+
def self.json_create(o)
|
399
|
+
o.delete JSON.create_id
|
400
|
+
self[o]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
class SubOpenStruct < OpenStruct
|
405
|
+
def [](k)
|
406
|
+
__send__(k)
|
407
|
+
end
|
408
|
+
|
409
|
+
def []=(k, v)
|
410
|
+
@item_set = true
|
411
|
+
__send__("#{k}=", v)
|
412
|
+
end
|
413
|
+
|
414
|
+
def item_set?
|
415
|
+
@item_set
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def test_parse_object_custom_hash_derived_class
|
420
|
+
res = parse('{"foo":"bar"}', :object_class => SubHash)
|
421
|
+
assert_equal({"foo" => "bar"}, res)
|
422
|
+
assert_equal(SubHash, res.class)
|
423
|
+
assert res.item_set?
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_parse_object_custom_non_hash_derived_class
|
427
|
+
res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
|
428
|
+
assert_equal "bar", res.foo
|
429
|
+
assert_equal(SubOpenStruct, res.class)
|
430
|
+
assert res.item_set?
|
431
|
+
end
|
432
|
+
|
433
|
+
def test_parse_generic_object
|
434
|
+
res = parse(
|
435
|
+
'{"foo":"bar", "baz":{}}',
|
436
|
+
:object_class => JSON::GenericObject
|
437
|
+
)
|
438
|
+
assert_equal(JSON::GenericObject, res.class)
|
439
|
+
assert_equal "bar", res.foo
|
440
|
+
assert_equal "bar", res["foo"]
|
441
|
+
assert_equal "bar", res[:foo]
|
442
|
+
assert_equal "bar", res.to_hash[:foo]
|
443
|
+
assert_equal(JSON::GenericObject, res.baz.class)
|
444
|
+
end
|
445
|
+
|
446
|
+
def test_generate_core_subclasses_with_new_to_json
|
447
|
+
obj = SubHash2["foo" => SubHash2["bar" => true]]
|
448
|
+
obj_json = JSON(obj)
|
449
|
+
obj_again = parse(obj_json, :create_additions => true)
|
450
|
+
assert_kind_of SubHash2, obj_again
|
451
|
+
assert_kind_of SubHash2, obj_again['foo']
|
452
|
+
assert obj_again['foo']['bar']
|
453
|
+
assert_equal obj, obj_again
|
454
|
+
assert_equal ["foo"],
|
455
|
+
JSON(JSON(SubArray2["foo"]), :create_additions => true)
|
456
|
+
end
|
457
|
+
|
458
|
+
def test_generate_core_subclasses_with_default_to_json
|
459
|
+
assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
|
460
|
+
assert_equal '["foo"]', JSON(SubArray["foo"])
|
461
|
+
end
|
462
|
+
|
463
|
+
def test_generate_of_core_subclasses
|
464
|
+
obj = SubHash["foo" => SubHash["bar" => true]]
|
465
|
+
obj_json = JSON(obj)
|
466
|
+
obj_again = JSON(obj_json)
|
467
|
+
assert_kind_of Hash, obj_again
|
468
|
+
assert_kind_of Hash, obj_again['foo']
|
469
|
+
assert obj_again['foo']['bar']
|
470
|
+
assert_equal obj, obj_again
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_parsing_frozen_ascii8bit_string
|
474
|
+
assert_equal(
|
475
|
+
{ 'foo' => 'bar' },
|
476
|
+
JSON('{ "foo": "bar" }'.force_encoding(Encoding::ASCII_8BIT).freeze)
|
477
|
+
)
|
478
|
+
end
|
479
|
+
|
480
|
+
private
|
481
|
+
|
482
|
+
def string_deduplication_available?
|
483
|
+
r1 = rand.to_s
|
484
|
+
r2 = r1.dup
|
485
|
+
begin
|
486
|
+
(-r1).equal?(-r2)
|
487
|
+
rescue NoMethodError
|
488
|
+
false # No String#-@
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def assert_equal_float(expected, actual, delta = 1e-2)
|
493
|
+
Array === expected and expected = expected.first
|
494
|
+
Array === actual and actual = actual.first
|
495
|
+
assert_in_delta(expected, actual, delta)
|
496
|
+
end
|
497
|
+
end
|