From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <jari.vetoniemi@indooratlas.com>
Date: Mon, 16 Mar 2020 18:49:26 +0900
Subject: Fresh start

---
 jni/ruby/test/json/fixtures/fail1.json          |   1 +
 jni/ruby/test/json/fixtures/fail10.json         |   1 +
 jni/ruby/test/json/fixtures/fail11.json         |   1 +
 jni/ruby/test/json/fixtures/fail12.json         |   1 +
 jni/ruby/test/json/fixtures/fail13.json         |   1 +
 jni/ruby/test/json/fixtures/fail14.json         |   1 +
 jni/ruby/test/json/fixtures/fail18.json         |   1 +
 jni/ruby/test/json/fixtures/fail19.json         |   1 +
 jni/ruby/test/json/fixtures/fail2.json          |   1 +
 jni/ruby/test/json/fixtures/fail20.json         |   1 +
 jni/ruby/test/json/fixtures/fail21.json         |   1 +
 jni/ruby/test/json/fixtures/fail22.json         |   1 +
 jni/ruby/test/json/fixtures/fail23.json         |   1 +
 jni/ruby/test/json/fixtures/fail24.json         |   1 +
 jni/ruby/test/json/fixtures/fail25.json         |   1 +
 jni/ruby/test/json/fixtures/fail27.json         |   2 +
 jni/ruby/test/json/fixtures/fail28.json         |   2 +
 jni/ruby/test/json/fixtures/fail3.json          |   1 +
 jni/ruby/test/json/fixtures/fail4.json          |   1 +
 jni/ruby/test/json/fixtures/fail5.json          |   1 +
 jni/ruby/test/json/fixtures/fail6.json          |   1 +
 jni/ruby/test/json/fixtures/fail7.json          |   1 +
 jni/ruby/test/json/fixtures/fail8.json          |   1 +
 jni/ruby/test/json/fixtures/fail9.json          |   1 +
 jni/ruby/test/json/fixtures/pass1.json          |  56 +++
 jni/ruby/test/json/fixtures/pass15.json         |   1 +
 jni/ruby/test/json/fixtures/pass16.json         |   1 +
 jni/ruby/test/json/fixtures/pass17.json         |   1 +
 jni/ruby/test/json/fixtures/pass2.json          |   1 +
 jni/ruby/test/json/fixtures/pass26.json         |   1 +
 jni/ruby/test/json/fixtures/pass3.json          |   6 +
 jni/ruby/test/json/setup_variant.rb             |  11 +
 jni/ruby/test/json/test_json.rb                 | 557 ++++++++++++++++++++++++
 jni/ruby/test/json/test_json_addition.rb        | 196 +++++++++
 jni/ruby/test/json/test_json_encoding.rb        |  65 +++
 jni/ruby/test/json/test_json_fixtures.rb        |  35 ++
 jni/ruby/test/json/test_json_generate.rb        | 322 ++++++++++++++
 jni/ruby/test/json/test_json_generic_object.rb  |  75 ++++
 jni/ruby/test/json/test_json_string_matching.rb |  39 ++
 jni/ruby/test/json/test_json_unicode.rb         |  72 +++
 40 files changed, 1465 insertions(+)
 create mode 100644 jni/ruby/test/json/fixtures/fail1.json
 create mode 100644 jni/ruby/test/json/fixtures/fail10.json
 create mode 100644 jni/ruby/test/json/fixtures/fail11.json
 create mode 100644 jni/ruby/test/json/fixtures/fail12.json
 create mode 100644 jni/ruby/test/json/fixtures/fail13.json
 create mode 100644 jni/ruby/test/json/fixtures/fail14.json
 create mode 100644 jni/ruby/test/json/fixtures/fail18.json
 create mode 100644 jni/ruby/test/json/fixtures/fail19.json
 create mode 100644 jni/ruby/test/json/fixtures/fail2.json
 create mode 100644 jni/ruby/test/json/fixtures/fail20.json
 create mode 100644 jni/ruby/test/json/fixtures/fail21.json
 create mode 100644 jni/ruby/test/json/fixtures/fail22.json
 create mode 100644 jni/ruby/test/json/fixtures/fail23.json
 create mode 100644 jni/ruby/test/json/fixtures/fail24.json
 create mode 100644 jni/ruby/test/json/fixtures/fail25.json
 create mode 100644 jni/ruby/test/json/fixtures/fail27.json
 create mode 100644 jni/ruby/test/json/fixtures/fail28.json
 create mode 100644 jni/ruby/test/json/fixtures/fail3.json
 create mode 100644 jni/ruby/test/json/fixtures/fail4.json
 create mode 100644 jni/ruby/test/json/fixtures/fail5.json
 create mode 100644 jni/ruby/test/json/fixtures/fail6.json
 create mode 100644 jni/ruby/test/json/fixtures/fail7.json
 create mode 100644 jni/ruby/test/json/fixtures/fail8.json
 create mode 100644 jni/ruby/test/json/fixtures/fail9.json
 create mode 100644 jni/ruby/test/json/fixtures/pass1.json
 create mode 100644 jni/ruby/test/json/fixtures/pass15.json
 create mode 100644 jni/ruby/test/json/fixtures/pass16.json
 create mode 100644 jni/ruby/test/json/fixtures/pass17.json
 create mode 100644 jni/ruby/test/json/fixtures/pass2.json
 create mode 100644 jni/ruby/test/json/fixtures/pass26.json
 create mode 100644 jni/ruby/test/json/fixtures/pass3.json
 create mode 100644 jni/ruby/test/json/setup_variant.rb
 create mode 100755 jni/ruby/test/json/test_json.rb
 create mode 100755 jni/ruby/test/json/test_json_addition.rb
 create mode 100644 jni/ruby/test/json/test_json_encoding.rb
 create mode 100755 jni/ruby/test/json/test_json_fixtures.rb
 create mode 100755 jni/ruby/test/json/test_json_generate.rb
 create mode 100644 jni/ruby/test/json/test_json_generic_object.rb
 create mode 100644 jni/ruby/test/json/test_json_string_matching.rb
 create mode 100755 jni/ruby/test/json/test_json_unicode.rb

(limited to 'jni/ruby/test/json')

diff --git a/jni/ruby/test/json/fixtures/fail1.json b/jni/ruby/test/json/fixtures/fail1.json
new file mode 100644
index 0000000..6216b86
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail1.json
@@ -0,0 +1 @@
+"A JSON payload should be an object or array, not a string."
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail10.json b/jni/ruby/test/json/fixtures/fail10.json
new file mode 100644
index 0000000..5d8c004
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail10.json
@@ -0,0 +1 @@
+{"Extra value after close": true} "misplaced quoted value"
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail11.json b/jni/ruby/test/json/fixtures/fail11.json
new file mode 100644
index 0000000..76eb95b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail11.json
@@ -0,0 +1 @@
+{"Illegal expression": 1 + 2}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail12.json b/jni/ruby/test/json/fixtures/fail12.json
new file mode 100644
index 0000000..77580a4
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail12.json
@@ -0,0 +1 @@
+{"Illegal invocation": alert()}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail13.json b/jni/ruby/test/json/fixtures/fail13.json
new file mode 100644
index 0000000..379406b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail13.json
@@ -0,0 +1 @@
+{"Numbers cannot have leading zeroes": 013}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail14.json b/jni/ruby/test/json/fixtures/fail14.json
new file mode 100644
index 0000000..0ed366b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail14.json
@@ -0,0 +1 @@
+{"Numbers cannot be hex": 0x14}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail18.json b/jni/ruby/test/json/fixtures/fail18.json
new file mode 100644
index 0000000..ebc11eb
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail18.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
diff --git a/jni/ruby/test/json/fixtures/fail19.json b/jni/ruby/test/json/fixtures/fail19.json
new file mode 100644
index 0000000..3b9c46f
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail19.json
@@ -0,0 +1 @@
+{"Missing colon" null}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail2.json b/jni/ruby/test/json/fixtures/fail2.json
new file mode 100644
index 0000000..6b7c11e
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail2.json
@@ -0,0 +1 @@
+["Unclosed array"
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail20.json b/jni/ruby/test/json/fixtures/fail20.json
new file mode 100644
index 0000000..27c1af3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail20.json
@@ -0,0 +1 @@
+{"Double colon":: null}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail21.json b/jni/ruby/test/json/fixtures/fail21.json
new file mode 100644
index 0000000..6247457
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail21.json
@@ -0,0 +1 @@
+{"Comma instead of colon", null}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail22.json b/jni/ruby/test/json/fixtures/fail22.json
new file mode 100644
index 0000000..a775258
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail22.json
@@ -0,0 +1 @@
+["Colon instead of comma": false]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail23.json b/jni/ruby/test/json/fixtures/fail23.json
new file mode 100644
index 0000000..494add1
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail23.json
@@ -0,0 +1 @@
+["Bad value", truth]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail24.json b/jni/ruby/test/json/fixtures/fail24.json
new file mode 100644
index 0000000..caff239
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail24.json
@@ -0,0 +1 @@
+['single quote']
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail25.json b/jni/ruby/test/json/fixtures/fail25.json
new file mode 100644
index 0000000..2dfbd25
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail25.json
@@ -0,0 +1 @@
+["tab	character	in	string	"]
diff --git a/jni/ruby/test/json/fixtures/fail27.json b/jni/ruby/test/json/fixtures/fail27.json
new file mode 100644
index 0000000..6b01a2c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail27.json
@@ -0,0 +1,2 @@
+["line
+break"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail28.json b/jni/ruby/test/json/fixtures/fail28.json
new file mode 100644
index 0000000..621a010
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail28.json
@@ -0,0 +1,2 @@
+["line\
+break"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail3.json b/jni/ruby/test/json/fixtures/fail3.json
new file mode 100644
index 0000000..168c81e
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail3.json
@@ -0,0 +1 @@
+{unquoted_key: "keys must be quoted"}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail4.json b/jni/ruby/test/json/fixtures/fail4.json
new file mode 100644
index 0000000..9de168b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail4.json
@@ -0,0 +1 @@
+["extra comma",]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail5.json b/jni/ruby/test/json/fixtures/fail5.json
new file mode 100644
index 0000000..ddf3ce3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail5.json
@@ -0,0 +1 @@
+["double extra comma",,]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail6.json b/jni/ruby/test/json/fixtures/fail6.json
new file mode 100644
index 0000000..ed91580
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail6.json
@@ -0,0 +1 @@
+[   , "<-- missing value"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail7.json b/jni/ruby/test/json/fixtures/fail7.json
new file mode 100644
index 0000000..8a96af3
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail7.json
@@ -0,0 +1 @@
+["Comma after the close"],
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail8.json b/jni/ruby/test/json/fixtures/fail8.json
new file mode 100644
index 0000000..b28479c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail8.json
@@ -0,0 +1 @@
+["Extra close"]]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/fail9.json b/jni/ruby/test/json/fixtures/fail9.json
new file mode 100644
index 0000000..5815574
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/fail9.json
@@ -0,0 +1 @@
+{"Extra comma": true,}
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass1.json b/jni/ruby/test/json/fixtures/pass1.json
new file mode 100644
index 0000000..7828fcc
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass1.json
@@ -0,0 +1,56 @@
+[
+    "JSON Test Pattern pass1",
+    {"object with 1 member":["array with 1 element"]},
+    {},
+    [],
+    -42,
+    true,
+    false,
+    null,
+    {
+        "integer": 1234567890,
+        "real": -9876.543210,
+        "e": 0.123456789e-12,
+        "E": 1.234567890E+34,
+        "":  23456789012E666,
+        "zero": 0,
+        "one": 1,
+        "space": " ",
+        "quote": "\"",
+        "backslash": "\\",
+        "controls": "\b\f\n\r\t",
+        "slash": "/ & \/",
+        "alpha": "abcdefghijklmnopqrstuvwyz",
+        "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
+        "digit": "0123456789",
+        "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
+        "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
+        "true": true,
+        "false": false,
+        "null": null,
+        "array":[  ],
+        "object":{  },
+        "address": "50 St. James Street",
+        "url": "http://www.JSON.org/",
+        "comment": "// /* <!-- --",
+        "# -- --> */": " ",
+        " s p a c e d " :[1,2 , 3
+
+,
+
+4 , 5        ,          6           ,7        ],
+        "compact": [1,2,3,4,5,6,7],
+        "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
+        "quotes": "&#34; \u0022 %22 0x22 034 &#x22;",
+        "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
+: "A key can be any string"
+    },
+    0.5 ,98.6
+,
+99.44
+,
+
+1066
+
+
+,"rosebud"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass15.json b/jni/ruby/test/json/fixtures/pass15.json
new file mode 100644
index 0000000..fc8376b
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass15.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \x15"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass16.json b/jni/ruby/test/json/fixtures/pass16.json
new file mode 100644
index 0000000..c43ae3c
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass16.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \'"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass17.json b/jni/ruby/test/json/fixtures/pass17.json
new file mode 100644
index 0000000..62b9214
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass17.json
@@ -0,0 +1 @@
+["Illegal backslash escape: \017"]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass2.json b/jni/ruby/test/json/fixtures/pass2.json
new file mode 100644
index 0000000..d3c63c7
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass2.json
@@ -0,0 +1 @@
+[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass26.json b/jni/ruby/test/json/fixtures/pass26.json
new file mode 100644
index 0000000..845d26a
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass26.json
@@ -0,0 +1 @@
+["tab\   character\   in\  string\  "]
\ No newline at end of file
diff --git a/jni/ruby/test/json/fixtures/pass3.json b/jni/ruby/test/json/fixtures/pass3.json
new file mode 100644
index 0000000..4528d51
--- /dev/null
+++ b/jni/ruby/test/json/fixtures/pass3.json
@@ -0,0 +1,6 @@
+{
+    "JSON Test Pattern pass3": {
+        "The outermost value": "must be an object or array.",
+        "In this test": "It is an object."
+    }
+}
diff --git a/jni/ruby/test/json/setup_variant.rb b/jni/ruby/test/json/setup_variant.rb
new file mode 100644
index 0000000..2dab184
--- /dev/null
+++ b/jni/ruby/test/json/setup_variant.rb
@@ -0,0 +1,11 @@
+case ENV['JSON']
+when 'pure'
+  $:.unshift 'lib'
+  require 'json/pure'
+when 'ext'
+  $:.unshift 'ext', 'lib'
+  require 'json/ext'
+else
+  $:.unshift 'ext', 'lib'
+  require 'json'
+end
diff --git a/jni/ruby/test/json/test_json.rb b/jni/ruby/test/json/test_json.rb
new file mode 100755
index 0000000..98ef970
--- /dev/null
+++ b/jni/ruby/test/json/test_json.rb
@@ -0,0 +1,557 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'stringio'
+require 'tempfile'
+require 'ostruct'
+
+unless Array.method_defined?(:permutation)
+  begin
+    require 'enumerator'
+    require 'permutation'
+    class Array
+      def permutation
+        Permutation.for(self).to_enum.map { |x| x.project }
+      end
+    end
+  rescue LoadError
+    warn "Skipping permutation tests."
+  end
+end
+
+class TestJSON < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true].map do
+      |x| [x]
+    end
+    @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
+      "[1,-2,3]", "false", "true"].map do
+      |x| "[#{x}]"
+    end
+    @hash = {
+      'a' => 2,
+      'b' => 3.141,
+      'c' => 'c',
+      'd' => [ 1, "b", 3.14 ],
+      'e' => { 'foo' => 'bar' },
+      'g' => "\"\0\037",
+      'h' => 1000.0,
+      'i' => 0.001
+    }
+    @json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},'\
+      '"g":"\\"\\u0000\\u001f","h":1.0E3,"i":1.0E-3}'
+  end
+
+  def test_construction
+    parser = JSON::Parser.new('test')
+    assert_equal 'test', parser.source
+  end
+
+  def assert_equal_float(expected, is)
+    assert_in_delta(expected.first, is.first, 1e-2)
+  end
+
+  def test_parse_simple_arrays
+    assert_equal([], parse('[]'))
+    assert_equal([], parse('  [  ] '))
+    assert_equal([nil], parse('[null]'))
+    assert_equal([false], parse('[false]'))
+    assert_equal([true], parse('[true]'))
+    assert_equal([-23], parse('[-23]'))
+    assert_equal([23], parse('[23]'))
+    assert_equal([0.23], parse('[0.23]'))
+    assert_equal([0.0], parse('[0e0]'))
+    assert_raises(JSON::ParserError) { parse('[+23.2]') }
+    assert_raises(JSON::ParserError) { parse('[+23]') }
+    assert_raises(JSON::ParserError) { parse('[.23]') }
+    assert_raises(JSON::ParserError) { parse('[023]') }
+    assert_equal_float [3.141], parse('[3.141]')
+    assert_equal_float [-3.141], parse('[-3.141]')
+    assert_equal_float [3.141], parse('[3141e-3]')
+    assert_equal_float [3.141], parse('[3141.1e-3]')
+    assert_equal_float [3.141], parse('[3141E-3]')
+    assert_equal_float [3.141], parse('[3141.0E-3]')
+    assert_equal_float [-3.141], parse('[-3141.0e-3]')
+    assert_equal_float [-3.141], parse('[-3141e-3]')
+    assert_raises(ParserError) { parse('[NaN]') }
+    assert parse('[NaN]', :allow_nan => true).first.nan?
+    assert_raises(ParserError) { parse('[Infinity]') }
+    assert_equal [1.0/0], parse('[Infinity]', :allow_nan => true)
+    assert_raises(ParserError) { parse('[-Infinity]') }
+    assert_equal [-1.0/0], parse('[-Infinity]', :allow_nan => true)
+    assert_equal([""], parse('[""]'))
+    assert_equal(["foobar"], parse('["foobar"]'))
+    assert_equal([{}], parse('[{}]'))
+  end
+
+  def test_parse_simple_objects
+    assert_equal({}, parse('{}'))
+    assert_equal({}, parse(' {   }   '))
+    assert_equal({ "a" => nil }, parse('{   "a"   :  null}'))
+    assert_equal({ "a" => nil }, parse('{"a":null}'))
+    assert_equal({ "a" => false }, parse('{   "a"  :  false  }  '))
+    assert_equal({ "a" => false }, parse('{"a":false}'))
+    assert_raises(JSON::ParserError) { parse('{false}') }
+    assert_equal({ "a" => true }, parse('{"a":true}'))
+    assert_equal({ "a" => true }, parse('  { "a" :  true  }   '))
+    assert_equal({ "a" => -23 }, parse('  {  "a"  :  -23  }  '))
+    assert_equal({ "a" => -23 }, parse('  { "a" : -23 } '))
+    assert_equal({ "a" => 23 }, parse('{"a":23  } '))
+    assert_equal({ "a" => 23 }, parse('  { "a"  : 23  } '))
+    assert_equal({ "a" => 0.23 }, parse(' { "a"  :  0.23 }  '))
+    assert_equal({ "a" => 0.23 }, parse('  {  "a"  :  0.23  }  '))
+  end
+
+  def test_parse_json_primitive_values
+    assert_raise(JSON::ParserError) { JSON.parse('') }
+    assert_raise(JSON::ParserError) { JSON.parse('', :quirks_mode => true) }
+    assert_raise(TypeError) { JSON::Parser.new(nil).parse }
+    assert_raise(TypeError) { JSON::Parser.new(nil, :quirks_mode => true).parse }
+    assert_raise(TypeError) { JSON.parse(nil) }
+    assert_raise(TypeError) { JSON.parse(nil, :quirks_mode => true) }
+    assert_raise(JSON::ParserError) { JSON.parse('  /* foo */ ') }
+    assert_raise(JSON::ParserError) { JSON.parse('  /* foo */ ', :quirks_mode => true) }
+    parser = JSON::Parser.new('null')
+    assert_equal false, parser.quirks_mode?
+    assert_raise(JSON::ParserError) { parser.parse }
+    assert_raise(JSON::ParserError) { JSON.parse('null') }
+    assert_equal nil, JSON.parse('null', :quirks_mode => true)
+    parser = JSON::Parser.new('null', :quirks_mode => true)
+    assert_equal true, parser.quirks_mode?
+    assert_equal nil, parser.parse
+    assert_raise(JSON::ParserError) { JSON.parse('false') }
+    assert_equal false, JSON.parse('false', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('true') }
+    assert_equal true, JSON.parse('true', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('23') }
+    assert_equal 23, JSON.parse('23', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('1') }
+    assert_equal 1, JSON.parse('1', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('3.141') }
+    assert_in_delta 3.141, JSON.parse('3.141', :quirks_mode => true), 1E-3
+    assert_raise(JSON::ParserError) { JSON.parse('18446744073709551616') }
+    assert_equal 2 ** 64, JSON.parse('18446744073709551616', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('"foo"') }
+    assert_equal 'foo', JSON.parse('"foo"', :quirks_mode => true)
+    assert_raise(JSON::ParserError) { JSON.parse('NaN', :allow_nan => true) }
+    assert JSON.parse('NaN', :quirks_mode => true, :allow_nan => true).nan?
+    assert_raise(JSON::ParserError) { JSON.parse('Infinity', :allow_nan => true) }
+    assert JSON.parse('Infinity', :quirks_mode => true, :allow_nan => true).infinite?
+    assert_raise(JSON::ParserError) { JSON.parse('-Infinity', :allow_nan => true) }
+    assert JSON.parse('-Infinity', :quirks_mode => true, :allow_nan => true).infinite?
+    assert_raise(JSON::ParserError) { JSON.parse('[ 1, ]', :quirks_mode => true) }
+  end
+
+  if Array.method_defined?(:permutation)
+    def test_parse_more_complex_arrays
+      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+      a.permutation.each do |perm|
+        json = pretty_generate(perm)
+        assert_equal perm, parse(json)
+      end
+    end
+
+    def test_parse_complex_objects
+      a = [ nil, false, true, "foßbar", [ "n€st€d", true ], { "nested" => true, "n€ßt€ð2" => {} }]
+      a.permutation.each do |perm|
+        s = "a"
+        orig_obj = perm.inject({}) { |h, x| h[s.dup] = x; s = s.succ; h }
+        json = pretty_generate(orig_obj)
+        assert_equal orig_obj, parse(json)
+      end
+    end
+  end
+
+  def test_parse_arrays
+    assert_equal([1,2,3], parse('[1,2,3]'))
+    assert_equal([1.2,2,3], parse('[1.2,2,3]'))
+    assert_equal([[],[[],[]]], parse('[[],[[],[]]]'))
+  end
+
+  def test_parse_values
+    assert_equal([""], parse('[""]'))
+    assert_equal(["\\"], parse('["\\\\"]'))
+    assert_equal(['"'], parse('["\""]'))
+    assert_equal(['\\"\\'], parse('["\\\\\\"\\\\"]'))
+    assert_equal(["\"\b\n\r\t\0\037"],
+      parse('["\"\b\n\r\t\u0000\u001f"]'))
+    for i in 0 ... @ary.size
+      assert_equal(@ary[i], parse(@ary_to_parse[i]))
+    end
+  end
+
+  def test_parse_array
+    assert_equal([], parse('[]'))
+    assert_equal([], parse('  [  ]  '))
+    assert_equal([1], parse('[1]'))
+    assert_equal([1], parse('  [ 1  ]  '))
+    assert_equal(@ary,
+      parse('[[1],["foo"],[3.14],[47.11e+2],[2718.0E-3],[null],[[1,-2,3]]'\
+      ',[false],[true]]'))
+    assert_equal(@ary, parse(%Q{   [   [1] , ["foo"]  ,  [3.14] \t ,  [47.11e+2]\s
+      , [2718.0E-3 ],\r[ null] , [[1, -2, 3 ]], [false ],[ true]\n ]  }))
+  end
+
+  class SubArray < Array
+    def <<(v)
+      @shifted = true
+      super
+    end
+
+    def shifted?
+      @shifted
+    end
+  end
+
+  class SubArray2 < Array
+    def to_json(*a)
+      {
+        JSON.create_id => self.class.name,
+        'ary'          => to_a,
+      }.to_json(*a)
+    end
+
+    def self.json_create(o)
+      o.delete JSON.create_id
+      o['ary']
+    end
+  end
+
+  class SubArrayWrapper
+    def initialize
+      @data = []
+    end
+
+    attr_reader :data
+
+    def [](index)
+      @data[index]
+    end
+
+    def <<(value)
+      @data << value
+      @shifted = true
+    end
+
+    def shifted?
+      @shifted
+    end
+  end
+
+  def test_parse_array_custom_array_derived_class
+    res = parse('[1,2]', :array_class => SubArray)
+    assert_equal([1,2], res)
+    assert_equal(SubArray, res.class)
+    assert res.shifted?
+  end
+
+  def test_parse_array_custom_non_array_derived_class
+    res = parse('[1,2]', :array_class => SubArrayWrapper)
+    assert_equal([1,2], res.data)
+    assert_equal(SubArrayWrapper, res.class)
+    assert res.shifted?
+  end
+
+  def test_parse_object
+    assert_equal({}, parse('{}'))
+    assert_equal({}, parse('  {  }  '))
+    assert_equal({'foo'=>'bar'}, parse('{"foo":"bar"}'))
+    assert_equal({'foo'=>'bar'}, parse('    { "foo"  :   "bar"   }   '))
+  end
+
+  class SubHash < Hash
+    def []=(k, v)
+      @item_set = true
+      super
+    end
+
+    def item_set?
+      @item_set
+    end
+  end
+
+  class SubHash2 < Hash
+    def to_json(*a)
+      {
+        JSON.create_id => self.class.name,
+      }.merge(self).to_json(*a)
+    end
+
+    def self.json_create(o)
+      o.delete JSON.create_id
+      self[o]
+    end
+  end
+
+  class SubOpenStruct < OpenStruct
+    def [](k)
+      __send__(k)
+    end
+
+    def []=(k, v)
+      @item_set = true
+      __send__("#{k}=", v)
+    end
+
+    def item_set?
+      @item_set
+    end
+  end
+
+  def test_parse_object_custom_hash_derived_class
+    res = parse('{"foo":"bar"}', :object_class => SubHash)
+    assert_equal({"foo" => "bar"}, res)
+    assert_equal(SubHash, res.class)
+    assert res.item_set?
+  end
+
+  def test_parse_object_custom_non_hash_derived_class
+    res = parse('{"foo":"bar"}', :object_class => SubOpenStruct)
+    assert_equal "bar", res.foo
+    assert_equal(SubOpenStruct, res.class)
+    assert res.item_set?
+  end
+
+  def test_parse_generic_object
+    res = parse('{"foo":"bar", "baz":{}}', :object_class => JSON::GenericObject)
+    assert_equal(JSON::GenericObject, res.class)
+    assert_equal "bar", res.foo
+    assert_equal "bar", res["foo"]
+    assert_equal "bar", res[:foo]
+    assert_equal "bar", res.to_hash[:foo]
+    assert_equal(JSON::GenericObject, res.baz.class)
+  end
+
+  def test_generate_core_subclasses_with_new_to_json
+    obj = SubHash2["foo" => SubHash2["bar" => true]]
+    obj_json = JSON(obj)
+    obj_again = JSON.parse(obj_json, :create_additions => true)
+    assert_kind_of SubHash2, obj_again
+    assert_kind_of SubHash2, obj_again['foo']
+    assert obj_again['foo']['bar']
+    assert_equal obj, obj_again
+    assert_equal ["foo"], JSON(JSON(SubArray2["foo"]), :create_additions => true)
+  end
+
+  def test_generate_core_subclasses_with_default_to_json
+    assert_equal '{"foo":"bar"}', JSON(SubHash["foo" => "bar"])
+    assert_equal '["foo"]', JSON(SubArray["foo"])
+  end
+
+  def test_generate_of_core_subclasses
+    obj = SubHash["foo" => SubHash["bar" => true]]
+    obj_json = JSON(obj)
+    obj_again = JSON(obj_json)
+    assert_kind_of Hash, obj_again
+    assert_kind_of Hash, obj_again['foo']
+    assert obj_again['foo']['bar']
+    assert_equal obj, obj_again
+  end
+
+  def test_parser_reset
+    parser = Parser.new(@json)
+    assert_equal(@hash, parser.parse)
+    assert_equal(@hash, parser.parse)
+  end
+
+  def test_comments
+    json = <<EOT
+{
+  "key1":"value1", // eol comment
+  "key2":"value2"  /* multi line
+                    *  comment */,
+  "key3":"value3"  /* multi line
+                    // nested eol comment
+                    *  comment */
+}
+EOT
+    assert_equal(
+      { "key1" => "value1", "key2" => "value2", "key3" => "value3" },
+      parse(json))
+    json = <<EOT
+{
+  "key1":"value1"  /* multi line
+                    // nested eol comment
+                    /* illegal nested multi line comment */
+                    *  comment */
+}
+EOT
+    assert_raises(ParserError) { parse(json) }
+    json = <<EOT
+{
+  "key1":"value1"  /* multi line
+                   // nested eol comment
+                   closed multi comment */
+                   and again, throw an Error */
+}
+EOT
+    assert_raises(ParserError) { parse(json) }
+    json = <<EOT
+{
+  "key1":"value1"  /*/*/
+}
+EOT
+    assert_equal({ "key1" => "value1" }, parse(json))
+  end
+
+  def test_backslash
+    data = [ '\\.(?i:gif|jpe?g|png)$' ]
+    json = '["\\\\.(?i:gif|jpe?g|png)$"]'
+    assert_equal json, JSON.generate(data)
+    assert_equal data, JSON.parse(json)
+    #
+    data = [ '\\"' ]
+    json = '["\\\\\""]'
+    assert_equal json, JSON.generate(data)
+    assert_equal data, JSON.parse(json)
+    #
+    json = '["/"]'
+    data = JSON.parse(json)
+    assert_equal ['/'], data
+    assert_equal json, JSON.generate(data)
+    #
+    json = '["\""]'
+    data = JSON.parse(json)
+    assert_equal ['"'], data
+    assert_equal json, JSON.generate(data)
+    json = '["\\\'"]'
+    data = JSON.parse(json)
+    assert_equal ["'"], data
+    assert_equal '["\'"]', JSON.generate(data)
+  end
+
+  def test_wrong_inputs
+    assert_raises(ParserError) { JSON.parse('"foo"') }
+    assert_raises(ParserError) { JSON.parse('123') }
+    assert_raises(ParserError) { JSON.parse('[] bla') }
+    assert_raises(ParserError) { JSON.parse('[] 1') }
+    assert_raises(ParserError) { JSON.parse('[] []') }
+    assert_raises(ParserError) { JSON.parse('[] {}') }
+    assert_raises(ParserError) { JSON.parse('{} []') }
+    assert_raises(ParserError) { JSON.parse('{} {}') }
+    assert_raises(ParserError) { JSON.parse('[NULL]') }
+    assert_raises(ParserError) { JSON.parse('[FALSE]') }
+    assert_raises(ParserError) { JSON.parse('[TRUE]') }
+    assert_raises(ParserError) { JSON.parse('[07]    ') }
+    assert_raises(ParserError) { JSON.parse('[0a]') }
+    assert_raises(ParserError) { JSON.parse('[1.]') }
+    assert_raises(ParserError) { JSON.parse('     ') }
+  end
+
+  def test_nesting
+    assert_raises(JSON::NestingError) { JSON.parse '[[]]', :max_nesting => 1 }
+    assert_raises(JSON::NestingError) { JSON.parser.new('[[]]', :max_nesting => 1).parse }
+    assert_equal [[]], JSON.parse('[[]]', :max_nesting => 2)
+    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+    too_deep_ary = eval too_deep
+    assert_raises(JSON::NestingError) { JSON.parse too_deep }
+    assert_raises(JSON::NestingError) { JSON.parser.new(too_deep).parse }
+    assert_raises(JSON::NestingError) { JSON.parse too_deep, :max_nesting => 100 }
+    ok = JSON.parse too_deep, :max_nesting => 101
+    assert_equal too_deep_ary, ok
+    ok = JSON.parse too_deep, :max_nesting => nil
+    assert_equal too_deep_ary, ok
+    ok = JSON.parse too_deep, :max_nesting => false
+    assert_equal too_deep_ary, ok
+    ok = JSON.parse too_deep, :max_nesting => 0
+    assert_equal too_deep_ary, ok
+    assert_raises(JSON::NestingError) { JSON.generate [[]], :max_nesting => 1 }
+    assert_equal '[[]]', JSON.generate([[]], :max_nesting => 2)
+    assert_raises(JSON::NestingError) { JSON.generate too_deep_ary }
+    assert_raises(JSON::NestingError) { JSON.generate too_deep_ary, :max_nesting => 100 }
+    ok = JSON.generate too_deep_ary, :max_nesting => 101
+    assert_equal too_deep, ok
+    ok = JSON.generate too_deep_ary, :max_nesting => nil
+    assert_equal too_deep, ok
+    ok = JSON.generate too_deep_ary, :max_nesting => false
+    assert_equal too_deep, ok
+    ok = JSON.generate too_deep_ary, :max_nesting => 0
+    assert_equal too_deep, ok
+  end
+
+  def test_symbolize_names
+    assert_equal({ "foo" => "bar", "baz" => "quux" },
+      JSON.parse('{"foo":"bar", "baz":"quux"}'))
+    assert_equal({ :foo => "bar", :baz => "quux" },
+      JSON.parse('{"foo":"bar", "baz":"quux"}', :symbolize_names => true))
+  end
+
+  def test_load
+    assert_equal @hash, JSON.load(@json)
+    tempfile = Tempfile.open('json')
+    tempfile.write @json
+    tempfile.rewind
+    assert_equal @hash, JSON.load(tempfile)
+    stringio = StringIO.new(@json)
+    stringio.rewind
+    assert_equal @hash, JSON.load(stringio)
+    assert_equal nil, JSON.load(nil)
+    assert_equal nil, JSON.load('')
+  ensure
+    tempfile.close!
+  end
+
+  def test_load_with_options
+    small_hash  = JSON("foo" => 'bar')
+    symbol_hash = { :foo => 'bar' }
+    assert_equal symbol_hash, JSON.load(small_hash, nil, :symbolize_names => true)
+  end
+
+  def test_dump
+    too_deep = '[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]'
+    assert_equal too_deep, JSON.dump(eval(too_deep))
+    assert_kind_of String, Marshal.dump(eval(too_deep))
+    assert_raises(ArgumentError) { JSON.dump(eval(too_deep), 100) }
+    assert_raises(ArgumentError) { Marshal.dump(eval(too_deep), 100) }
+    assert_equal too_deep, JSON.dump(eval(too_deep), 101)
+    assert_kind_of String, Marshal.dump(eval(too_deep), 101)
+    output = StringIO.new
+    JSON.dump(eval(too_deep), output)
+    assert_equal too_deep, output.string
+    output = StringIO.new
+    JSON.dump(eval(too_deep), output, 101)
+    assert_equal too_deep, output.string
+  end
+
+  def test_big_integers
+    json1 = JSON([orig = (1 << 31) - 1])
+    assert_equal orig, JSON[json1][0]
+    json2 = JSON([orig = 1 << 31])
+    assert_equal orig, JSON[json2][0]
+    json3 = JSON([orig = (1 << 62) - 1])
+    assert_equal orig, JSON[json3][0]
+    json4 = JSON([orig = 1 << 62])
+    assert_equal orig, JSON[json4][0]
+    json5 = JSON([orig = 1 << 64])
+    assert_equal orig, JSON[json5][0]
+  end
+
+  if defined?(JSON::Ext::Parser)
+    def test_allocate
+      parser = JSON::Ext::Parser.new("{}")
+      assert_raise(TypeError, '[ruby-core:35079]') {parser.__send__(:initialize, "{}")}
+      parser = JSON::Ext::Parser.allocate
+      assert_raise(TypeError, '[ruby-core:35079]') {parser.source}
+    end
+  end
+
+  def test_argument_encoding
+    source = "{}".force_encoding("ascii-8bit")
+    JSON::Parser.new(source)
+    assert_equal Encoding::ASCII_8BIT, source.encoding
+  end if defined?(Encoding::ASCII_8BIT)
+
+  def test_error_message_encoding
+    bug10705 = '[ruby-core:67386] [Bug #10705]'
+    json = "\"\xE2\x88\x9A\"".force_encoding(Encoding::UTF_8)
+    e = assert_raise(JSON::ParserError) {
+      JSON.parse(json)
+    }
+    assert_equal(Encoding::UTF_8, e.message.encoding, bug10705)
+    assert_include(e.message, json, bug10705)
+  end if defined?(Encoding::UTF_8)
+end
diff --git a/jni/ruby/test/json/test_json_addition.rb b/jni/ruby/test/json/test_json_addition.rb
new file mode 100755
index 0000000..a30f06a
--- /dev/null
+++ b/jni/ruby/test/json/test_json_addition.rb
@@ -0,0 +1,196 @@
+#!/usr/bin/env ruby
+# -*- coding:utf-8 -*-
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'json/add/core'
+require 'json/add/complex'
+require 'json/add/rational'
+require 'json/add/bigdecimal'
+require 'json/add/ostruct'
+require 'date'
+
+class TestJSONAddition < Test::Unit::TestCase
+  include JSON
+
+  class A
+    def initialize(a)
+      @a = a
+    end
+
+    attr_reader :a
+
+    def ==(other)
+      a == other.a
+    end
+
+    def self.json_create(object)
+      new(*object['args'])
+    end
+
+    def to_json(*args)
+      {
+        'json_class'  => self.class.name,
+        'args'        => [ @a ],
+      }.to_json(*args)
+    end
+  end
+
+  class A2 < A
+    def to_json(*args)
+      {
+        'json_class'  => self.class.name,
+        'args'        => [ @a ],
+      }.to_json(*args)
+    end
+  end
+
+  class B
+    def self.json_creatable?
+      false
+    end
+
+    def to_json(*args)
+      {
+        'json_class'  => self.class.name,
+      }.to_json(*args)
+    end
+  end
+
+  class C
+    def self.json_creatable?
+      false
+    end
+
+    def to_json(*args)
+      {
+        'json_class'  => 'TestJSONAddition::Nix',
+      }.to_json(*args)
+    end
+  end
+
+  def test_extended_json
+    a = A.new(666)
+    assert A.json_creatable?
+    json = generate(a)
+    a_again = JSON.parse(json, :create_additions => true)
+    assert_kind_of a.class, a_again
+    assert_equal a, a_again
+  end
+
+  def test_extended_json_default
+    a = A.new(666)
+    assert A.json_creatable?
+    json = generate(a)
+    a_hash = JSON.parse(json)
+    assert_kind_of Hash, a_hash
+  end
+
+  def test_extended_json_disabled
+    a = A.new(666)
+    assert A.json_creatable?
+    json = generate(a)
+    a_again = JSON.parse(json, :create_additions => true)
+    assert_kind_of a.class, a_again
+    assert_equal a, a_again
+    a_hash = JSON.parse(json, :create_additions => false)
+    assert_kind_of Hash, a_hash
+    assert_equal(
+      {"args"=>[666], "json_class"=>"TestJSONAddition::A"}.sort_by { |k,| k },
+      a_hash.sort_by { |k,| k }
+    )
+  end
+
+  def test_extended_json_fail1
+    b = B.new
+    assert !B.json_creatable?
+    json = generate(b)
+    assert_equal({ "json_class"=>"TestJSONAddition::B" }, JSON.parse(json))
+  end
+
+  def test_extended_json_fail2
+    c = C.new
+    assert !C.json_creatable?
+    json = generate(c)
+    assert_raises(ArgumentError, NameError) { JSON.parse(json, :create_additions => true) }
+  end
+
+  def test_raw_strings
+    raw = ''
+    raw.respond_to?(:encode!) and raw.encode!(Encoding::ASCII_8BIT)
+    raw_array = []
+    for i in 0..255
+      raw << i
+      raw_array << i
+    end
+    json = raw.to_json_raw
+    json_raw_object = raw.to_json_raw_object
+    hash = { 'json_class' => 'String', 'raw'=> raw_array }
+    assert_equal hash, json_raw_object
+    assert_match(/\A\{.*\}\z/, json)
+    assert_match(/"json_class":"String"/, json)
+    assert_match(/"raw":\[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255\]/, json)
+    raw_again = JSON.parse(json, :create_additions => true)
+    assert_equal raw, raw_again
+  end
+
+  MyJsonStruct = Struct.new 'MyJsonStruct', :foo, :bar
+
+  def test_core
+    t = Time.now
+    assert_equal t, JSON(JSON(t), :create_additions => true)
+    d = Date.today
+    assert_equal d, JSON(JSON(d), :create_additions => true)
+    d = DateTime.civil(2007, 6, 14, 14, 57, 10, Rational(1, 12), 2299161)
+    assert_equal d, JSON(JSON(d), :create_additions => true)
+    assert_equal 1..10, JSON(JSON(1..10), :create_additions => true)
+    assert_equal 1...10, JSON(JSON(1...10), :create_additions => true)
+    assert_equal "a".."c", JSON(JSON("a".."c"), :create_additions => true)
+    assert_equal "a"..."c", JSON(JSON("a"..."c"), :create_additions => true)
+    s = MyJsonStruct.new 4711, 'foot'
+    assert_equal s, JSON(JSON(s), :create_additions => true)
+    struct = Struct.new :foo, :bar
+    s = struct.new 4711, 'foot'
+    assert_raises(JSONError) { JSON(s) }
+    begin
+      raise TypeError, "test me"
+    rescue TypeError => e
+      e_json = JSON.generate e
+      e_again = JSON e_json, :create_additions => true
+      assert_kind_of TypeError, e_again
+      assert_equal e.message, e_again.message
+      assert_equal e.backtrace, e_again.backtrace
+    end
+    assert_equal(/foo/, JSON(JSON(/foo/), :create_additions => true))
+    assert_equal(/foo/i, JSON(JSON(/foo/i), :create_additions => true))
+  end
+
+  def test_utc_datetime
+    now = Time.now
+    d = DateTime.parse(now.to_s, :create_additions => true)                    # usual case
+    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    d = DateTime.parse(now.utc.to_s)                # of = 0
+    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(1,24))
+    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+    d = DateTime.civil(2008, 6, 17, 11, 48, 32, Rational(12,24))
+    assert_equal d, JSON.parse(d.to_json, :create_additions => true)
+  end
+
+  def test_rational_complex
+    assert_equal Rational(2, 9), JSON.parse(JSON(Rational(2, 9)), :create_additions => true)
+    assert_equal Complex(2, 9), JSON.parse(JSON(Complex(2, 9)), :create_additions => true)
+  end
+
+  def test_bigdecimal
+    assert_equal BigDecimal('3.141', 23), JSON(JSON(BigDecimal('3.141', 23)), :create_additions => true)
+    assert_equal BigDecimal('3.141', 666), JSON(JSON(BigDecimal('3.141', 666)), :create_additions => true)
+  end
+
+  def test_ostruct
+    o = OpenStruct.new
+    # XXX this won't work; o.foo = { :bar => true }
+    o.foo = { 'bar' => true }
+    assert_equal o, JSON.parse(JSON(o), :create_additions => true)
+  end
+end
diff --git a/jni/ruby/test/json/test_json_encoding.rb b/jni/ruby/test/json/test_json_encoding.rb
new file mode 100644
index 0000000..fa7d878
--- /dev/null
+++ b/jni/ruby/test/json/test_json_encoding.rb
@@ -0,0 +1,65 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONEncoding < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @utf_8 = '["© ≠ €!"]'
+    @parsed = [ "© ≠ €!" ]
+    @generated = '["\u00a9 \u2260 \u20ac!"]'
+    if String.method_defined?(:encode)
+      @utf_16_data = [@parsed.first.encode('utf-16be', 'utf-8')]
+      @utf_8_ascii_8bit = @utf_8.dup.force_encoding(Encoding::ASCII_8BIT)
+      @utf_16be = @utf_8.encode('utf-16be', 'utf-8')
+      @utf_16be_ascii_8bit = @utf_16be.dup.force_encoding(Encoding::ASCII_8BIT)
+      @utf_16le = @utf_8.encode('utf-16le', 'utf-8')
+      @utf_16le_ascii_8bit = @utf_16le.dup.force_encoding(Encoding::ASCII_8BIT)
+      @utf_32be = @utf_8.encode('utf-32be', 'utf-8')
+      @utf_32be_ascii_8bit = @utf_32be.dup.force_encoding(Encoding::ASCII_8BIT)
+      @utf_32le = @utf_8.encode('utf-32le', 'utf-8')
+      @utf_32le_ascii_8bit = @utf_32le.dup.force_encoding(Encoding::ASCII_8BIT)
+    else
+      require 'iconv'
+      @utf_16_data = Iconv.iconv('utf-16be', 'utf-8', @parsed.first)
+      @utf_8_ascii_8bit = @utf_8.dup
+      @utf_16be, = Iconv.iconv('utf-16be', 'utf-8', @utf_8)
+      @utf_16be_ascii_8bit = @utf_16be.dup
+      @utf_16le, = Iconv.iconv('utf-16le', 'utf-8', @utf_8)
+      @utf_16le_ascii_8bit = @utf_16le.dup
+      @utf_32be, = Iconv.iconv('utf-32be', 'utf-8', @utf_8)
+      @utf_32be_ascii_8bit = @utf_32be.dup
+      @utf_32le, = Iconv.iconv('utf-32le', 'utf-8', @utf_8)
+      @utf_32le_ascii_8bit = @utf_32le.dup
+    end
+  end
+
+  def test_parse
+    assert_equal @parsed, JSON.parse(@utf_8)
+    assert_equal @parsed, JSON.parse(@utf_16be)
+    assert_equal @parsed, JSON.parse(@utf_16le)
+    assert_equal @parsed, JSON.parse(@utf_32be)
+    assert_equal @parsed, JSON.parse(@utf_32le)
+  end
+
+  def test_parse_ascii_8bit
+    assert_equal @parsed, JSON.parse(@utf_8_ascii_8bit)
+    assert_equal @parsed, JSON.parse(@utf_16be_ascii_8bit)
+    assert_equal @parsed, JSON.parse(@utf_16le_ascii_8bit)
+    assert_equal @parsed, JSON.parse(@utf_32be_ascii_8bit)
+    assert_equal @parsed, JSON.parse(@utf_32le_ascii_8bit)
+  end
+
+  def test_generate
+    assert_equal @generated, JSON.generate(@parsed, :ascii_only => true)
+    if defined?(::Encoding)
+      assert_equal @generated, JSON.generate(@utf_16_data, :ascii_only => true)
+    else
+      # XXX checking of correct utf8 data is not as strict (yet?) without :ascii_only
+      assert_raises(JSON::GeneratorError) { JSON.generate(@utf_16_data, :ascii_only => true) }
+    end
+  end
+end
diff --git a/jni/ruby/test/json/test_json_fixtures.rb b/jni/ruby/test/json/test_json_fixtures.rb
new file mode 100755
index 0000000..584dffd
--- /dev/null
+++ b/jni/ruby/test/json/test_json_fixtures.rb
@@ -0,0 +1,35 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONFixtures < Test::Unit::TestCase
+  def setup
+    fixtures = File.join(File.dirname(__FILE__), 'fixtures/*.json')
+    passed, failed = Dir[fixtures].partition { |f| f['pass'] }
+    @passed = passed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
+    @failed = failed.inject([]) { |a, f| a << [ f, File.read(f) ] }.sort
+  end
+
+  def test_passing
+    for name, source in @passed
+      begin
+        assert JSON.parse(source),
+          "Did not pass for fixture '#{name}': #{source.inspect}"
+      rescue => e
+        warn "\nCaught #{e.class}(#{e}) for fixture '#{name}': #{source.inspect}\n#{e.backtrace * "\n"}"
+        raise e
+      end
+    end
+  end
+
+  def test_failing
+    for name, source in @failed
+      assert_raises(JSON::ParserError, JSON::NestingError,
+        "Did not fail for fixture '#{name}': #{source.inspect}") do
+        JSON.parse(source)
+      end
+    end
+  end
+end
diff --git a/jni/ruby/test/json/test_json_generate.rb b/jni/ruby/test/json/test_json_generate.rb
new file mode 100755
index 0000000..2d4e1ee
--- /dev/null
+++ b/jni/ruby/test/json/test_json_generate.rb
@@ -0,0 +1,322 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONGenerate < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @hash = {
+      'a' => 2,
+      'b' => 3.141,
+      'c' => 'c',
+      'd' => [ 1, "b", 3.14 ],
+      'e' => { 'foo' => 'bar' },
+      'g' => "\"\0\037",
+      'h' => 1000.0,
+      'i' => 0.001
+    }
+    @json2 = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
+      '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
+    @json3 = <<'EOT'.chomp
+{
+  "a": 2,
+  "b": 3.141,
+  "c": "c",
+  "d": [
+    1,
+    "b",
+    3.14
+  ],
+  "e": {
+    "foo": "bar"
+  },
+  "g": "\"\u0000\u001f",
+  "h": 1000.0,
+  "i": 0.001
+}
+EOT
+  end
+
+  def test_generate
+    json = generate(@hash)
+    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    json = JSON[@hash]
+    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    parsed_json = parse(json)
+    assert_equal(@hash, parsed_json)
+    json = generate({1=>2})
+    assert_equal('{"1":2}', json)
+    parsed_json = parse(json)
+    assert_equal({"1"=>2}, parsed_json)
+    assert_raise(GeneratorError) { generate(666) }
+    assert_equal '666', generate(666, :quirks_mode => true)
+  end
+
+  def test_generate_pretty
+    json = pretty_generate(@hash)
+    # hashes aren't (insertion) ordered on every ruby implementation assert_equal(@json3, json)
+    assert_equal(JSON.parse(@json3), JSON.parse(json))
+    parsed_json = parse(json)
+    assert_equal(@hash, parsed_json)
+    json = pretty_generate({1=>2})
+    assert_equal(<<'EOT'.chomp, json)
+{
+  "1": 2
+}
+EOT
+    parsed_json = parse(json)
+    assert_equal({"1"=>2}, parsed_json)
+    assert_raise(GeneratorError) { pretty_generate(666) }
+    assert_equal '666', pretty_generate(666, :quirks_mode => true)
+  end
+
+  def test_fast_generate
+    json = fast_generate(@hash)
+    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    parsed_json = parse(json)
+    assert_equal(@hash, parsed_json)
+    json = fast_generate({1=>2})
+    assert_equal('{"1":2}', json)
+    parsed_json = parse(json)
+    assert_equal({"1"=>2}, parsed_json)
+    assert_raise(GeneratorError) { fast_generate(666) }
+    assert_equal '666', fast_generate(666, :quirks_mode => true)
+  end
+
+  def test_own_state
+    state = State.new
+    json = generate(@hash, state)
+    assert_equal(JSON.parse(@json2), JSON.parse(json))
+    parsed_json = parse(json)
+    assert_equal(@hash, parsed_json)
+    json = generate({1=>2}, state)
+    assert_equal('{"1":2}', json)
+    parsed_json = parse(json)
+    assert_equal({"1"=>2}, parsed_json)
+    assert_raise(GeneratorError) { generate(666, state) }
+    state.quirks_mode = true
+    assert state.quirks_mode?
+    assert_equal '666', generate(666, state)
+  end
+
+  def test_states
+    json = generate({1=>2}, nil)
+    assert_equal('{"1":2}', json)
+    s = JSON.state.new
+    assert s.check_circular?
+    assert s[:check_circular?]
+    h = { 1=>2 }
+    h[3] = h
+    assert_raises(JSON::NestingError) {  generate(h) }
+    assert_raises(JSON::NestingError) {  generate(h, s) }
+    s = JSON.state.new
+    a = [ 1, 2 ]
+    a << a
+    assert_raises(JSON::NestingError) {  generate(a, s) }
+    assert s.check_circular?
+    assert s[:check_circular?]
+  end
+
+  def test_pretty_state
+    state = PRETTY_STATE_PROTOTYPE.dup
+    assert_equal({
+      :allow_nan             => false,
+      :array_nl              => "\n",
+      :ascii_only            => false,
+      :buffer_initial_length => 1024,
+      :quirks_mode           => false,
+      :depth                 => 0,
+      :indent                => "  ",
+      :max_nesting           => 100,
+      :object_nl             => "\n",
+      :space                 => " ",
+      :space_before          => "",
+    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+  end
+
+  def test_safe_state
+    state = SAFE_STATE_PROTOTYPE.dup
+    assert_equal({
+      :allow_nan             => false,
+      :array_nl              => "",
+      :ascii_only            => false,
+      :buffer_initial_length => 1024,
+      :quirks_mode           => false,
+      :depth                 => 0,
+      :indent                => "",
+      :max_nesting           => 100,
+      :object_nl             => "",
+      :space                 => "",
+      :space_before          => "",
+    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+  end
+
+  def test_fast_state
+    state = FAST_STATE_PROTOTYPE.dup
+    assert_equal({
+      :allow_nan             => false,
+      :array_nl              => "",
+      :ascii_only            => false,
+      :buffer_initial_length => 1024,
+      :quirks_mode           => false,
+      :depth                 => 0,
+      :indent                => "",
+      :max_nesting           => 0,
+      :object_nl             => "",
+      :space                 => "",
+      :space_before          => "",
+    }.sort_by { |n,| n.to_s }, state.to_h.sort_by { |n,| n.to_s })
+  end
+
+  def test_allow_nan
+    assert_raises(GeneratorError) { generate([JSON::NaN]) }
+    assert_equal '[NaN]', generate([JSON::NaN], :allow_nan => true)
+    assert_raises(GeneratorError) { fast_generate([JSON::NaN]) }
+    assert_raises(GeneratorError) { pretty_generate([JSON::NaN]) }
+    assert_equal "[\n  NaN\n]", pretty_generate([JSON::NaN], :allow_nan => true)
+    assert_raises(GeneratorError) { generate([JSON::Infinity]) }
+    assert_equal '[Infinity]', generate([JSON::Infinity], :allow_nan => true)
+    assert_raises(GeneratorError) { fast_generate([JSON::Infinity]) }
+    assert_raises(GeneratorError) { pretty_generate([JSON::Infinity]) }
+    assert_equal "[\n  Infinity\n]", pretty_generate([JSON::Infinity], :allow_nan => true)
+    assert_raises(GeneratorError) { generate([JSON::MinusInfinity]) }
+    assert_equal '[-Infinity]', generate([JSON::MinusInfinity], :allow_nan => true)
+    assert_raises(GeneratorError) { fast_generate([JSON::MinusInfinity]) }
+    assert_raises(GeneratorError) { pretty_generate([JSON::MinusInfinity]) }
+    assert_equal "[\n  -Infinity\n]", pretty_generate([JSON::MinusInfinity], :allow_nan => true)
+  end
+
+  def test_depth
+    ary = []; ary << ary
+    assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
+    assert_raises(JSON::NestingError) { JSON.generate(ary) }
+    assert_equal 0, JSON::SAFE_STATE_PROTOTYPE.depth
+    assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
+    assert_raises(JSON::NestingError) { JSON.pretty_generate(ary) }
+    assert_equal 0, JSON::PRETTY_STATE_PROTOTYPE.depth
+    s = JSON.state.new
+    assert_equal 0, s.depth
+    assert_raises(JSON::NestingError) { ary.to_json(s) }
+    assert_equal 100, s.depth
+  end
+
+  def test_buffer_initial_length
+    s = JSON.state.new
+    assert_equal 1024, s.buffer_initial_length
+    s.buffer_initial_length = 0
+    assert_equal 1024, s.buffer_initial_length
+    s.buffer_initial_length = -1
+    assert_equal 1024, s.buffer_initial_length
+    s.buffer_initial_length = 128
+    assert_equal 128, s.buffer_initial_length
+  end
+
+  def test_gc
+    assert_in_out_err(%w[-rjson --disable-gems], <<-EOS, [], [])
+      bignum_too_long_to_embed_as_string = 1234567890123456789012345
+      expect = bignum_too_long_to_embed_as_string.to_s
+      GC.stress = true
+
+      10.times do |i|
+        tmp = bignum_too_long_to_embed_as_string.to_json
+        raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
+      end
+    EOS
+  end if GC.respond_to?(:stress=)
+
+  def test_configure_using_configure_and_merge
+    numbered_state = {
+      :indent       => "1",
+      :space        => '2',
+      :space_before => '3',
+      :object_nl    => '4',
+      :array_nl     => '5'
+    }
+    state1 = JSON.state.new
+    state1.merge(numbered_state)
+    assert_equal '1', state1.indent
+    assert_equal '2', state1.space
+    assert_equal '3', state1.space_before
+    assert_equal '4', state1.object_nl
+    assert_equal '5', state1.array_nl
+    state2 = JSON.state.new
+    state2.configure(numbered_state)
+    assert_equal '1', state2.indent
+    assert_equal '2', state2.space
+    assert_equal '3', state2.space_before
+    assert_equal '4', state2.object_nl
+    assert_equal '5', state2.array_nl
+  end
+
+  def test_configure_hash_conversion
+    state = JSON.state.new
+    state.configure(:indent => '1')
+    assert_equal '1', state.indent
+    state = JSON.state.new
+    foo = 'foo'
+    assert_raise(TypeError) do
+      state.configure(foo)
+    end
+    def foo.to_h
+      { :indent => '2' }
+    end
+    state.configure(foo)
+    assert_equal '2', state.indent
+  end
+
+  if defined?(JSON::Ext::Generator)
+    def test_broken_bignum # [ruby-core:38867]
+      pid = fork do
+        Bignum.class_eval do
+          def to_s
+          end
+        end
+        begin
+          JSON::Ext::Generator::State.new.generate(1<<64)
+          exit 1
+        rescue TypeError
+          exit 0
+        end
+      end
+      _, status = Process.waitpid2(pid)
+      assert status.success?
+    rescue NotImplementedError
+      # forking to avoid modifying core class of a parent process and
+      # introducing race conditions of tests are run in parallel
+    end
+  end
+
+  def test_hash_likeness_set_symbol
+    state = JSON.state.new
+    assert_equal nil, state[:foo]
+    assert_equal nil.class, state[:foo].class
+    assert_equal nil, state['foo']
+    state[:foo] = :bar
+    assert_equal :bar, state[:foo]
+    assert_equal :bar, state['foo']
+    state_hash = state.to_hash
+    assert_kind_of Hash, state_hash
+    assert_equal :bar, state_hash[:foo]
+  end
+
+  def test_hash_likeness_set_string
+    state = JSON.state.new
+    assert_equal nil, state[:foo]
+    assert_equal nil, state['foo']
+    state['foo'] = :bar
+    assert_equal :bar, state[:foo]
+    assert_equal :bar, state['foo']
+    state_hash = state.to_hash
+    assert_kind_of Hash, state_hash
+    assert_equal :bar, state_hash[:foo]
+  end
+
+  def test_json_generate
+    assert_raise JSON::GeneratorError do
+      assert_equal true, JSON.generate(["\xea"])
+    end
+  end
+end
diff --git a/jni/ruby/test/json/test_json_generic_object.rb b/jni/ruby/test/json/test_json_generic_object.rb
new file mode 100644
index 0000000..c43c776
--- /dev/null
+++ b/jni/ruby/test/json/test_json_generic_object.rb
@@ -0,0 +1,75 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+class TestJSONGenericObject < Test::Unit::TestCase
+  include JSON
+
+  def setup
+    @go = GenericObject[ :a => 1, :b => 2 ]
+  end
+
+  def test_attributes
+    assert_equal 1, @go.a
+    assert_equal 1, @go[:a]
+    assert_equal 2, @go.b
+    assert_equal 2, @go[:b]
+    assert_nil @go.c
+    assert_nil @go[:c]
+  end
+
+  def test_generate_json
+    switch_json_creatable do
+      assert_equal @go, JSON(JSON(@go), :create_additions => true)
+    end
+  end
+
+  def test_parse_json
+    assert_kind_of Hash, JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+    switch_json_creatable do
+      assert_equal @go, l = JSON('{ "json_class": "JSON::GenericObject", "a": 1, "b": 2 }', :create_additions => true)
+      assert_equal 1, l.a
+      assert_equal @go, l = JSON('{ "a": 1, "b": 2 }', :object_class => GenericObject)
+      assert_equal 1, l.a
+      assert_equal GenericObject[:a => GenericObject[:b => 2]],
+        l = JSON('{ "a": { "b": 2 } }', :object_class => GenericObject)
+      assert_equal 2, l.a.b
+    end
+  end
+
+  def test_from_hash
+    result  = GenericObject.from_hash(
+      :foo => { :bar => { :baz => true }, :quux => [ { :foobar => true } ] })
+    assert_kind_of GenericObject, result.foo
+    assert_kind_of GenericObject, result.foo.bar
+    assert_equal   true, result.foo.bar.baz
+    assert_kind_of GenericObject, result.foo.quux.first
+    assert_equal   true, result.foo.quux.first.foobar
+    assert_equal   true, GenericObject.from_hash(true)
+  end
+
+  def test_json_generic_object_load
+    empty = JSON::GenericObject.load(nil)
+    assert_kind_of JSON::GenericObject, empty
+    simple_json = '{"json_class":"JSON::GenericObject","hello":"world"}'
+    simple = JSON::GenericObject.load(simple_json)
+    assert_kind_of JSON::GenericObject, simple
+    assert_equal "world", simple.hello
+    converting = JSON::GenericObject.load('{ "hello": "world" }')
+    assert_kind_of JSON::GenericObject, converting
+    assert_equal "world", converting.hello
+
+    json = JSON::GenericObject.dump(JSON::GenericObject[:hello => 'world'])
+    assert_equal JSON(json), JSON('{"json_class":"JSON::GenericObject","hello":"world"}')
+  end
+
+  private
+
+  def switch_json_creatable
+    JSON::GenericObject.json_creatable = true
+    yield
+  ensure
+    JSON::GenericObject.json_creatable = false
+  end
+end
diff --git a/jni/ruby/test/json/test_json_string_matching.rb b/jni/ruby/test/json/test_json_string_matching.rb
new file mode 100644
index 0000000..c233df8
--- /dev/null
+++ b/jni/ruby/test/json/test_json_string_matching.rb
@@ -0,0 +1,39 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+require 'stringio'
+require 'time'
+
+class TestJSONStringMatching < Test::Unit::TestCase
+  include JSON
+
+  class TestTime < ::Time
+    def self.json_create(string)
+      Time.parse(string)
+    end
+
+    def to_json(*)
+      %{"#{strftime('%FT%T%z')}"}
+    end
+
+    def ==(other)
+      to_i == other.to_i
+    end
+  end
+
+  def test_match_date
+    t = TestTime.new
+    t_json = [ t ].to_json
+    assert_equal [ t ],
+      JSON.parse(t_json, :create_additions => true,
+        :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+    assert_equal [ t.strftime('%FT%T%z') ],
+      JSON.parse(t_json, :create_additions => true,
+        :match_string => { /\A\d{3}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+    assert_equal [ t.strftime('%FT%T%z') ],
+      JSON.parse(t_json,
+        :match_string => { /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{4}\z/ => TestTime })
+  end
+end
diff --git a/jni/ruby/test/json/test_json_unicode.rb b/jni/ruby/test/json/test_json_unicode.rb
new file mode 100755
index 0000000..8352d5c
--- /dev/null
+++ b/jni/ruby/test/json/test_json_unicode.rb
@@ -0,0 +1,72 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+
+require 'test/unit'
+require File.join(File.dirname(__FILE__), 'setup_variant')
+
+class TestJSONUnicode < Test::Unit::TestCase
+  include JSON
+
+  def test_unicode
+    assert_equal '""', ''.to_json
+    assert_equal '"\\b"', "\b".to_json
+    assert_equal '"\u0001"', 0x1.chr.to_json
+    assert_equal '"\u001f"', 0x1f.chr.to_json
+    assert_equal '" "', ' '.to_json
+    assert_equal "\"#{0x7f.chr}\"", 0x7f.chr.to_json
+    utf8 = [ "© ≠ €! \01" ]
+    json = '["© ≠ €! \u0001"]'
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    assert_equal utf8, parse(json)
+    json = '["\u00a9 \u2260 \u20ac! \u0001"]'
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+    json = "[\"\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212\"]"
+    assert_equal utf8, parse(json)
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    utf8 = ["\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"]
+    assert_equal utf8, parse(json)
+    json = "[\"\\u3042\\u3044\\u3046\\u3048\\u304a\"]"
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    utf8 = ['საქართველო']
+    json = '["საქართველო"]'
+    assert_equal json, utf8.to_json(:ascii_only => false)
+    json = "[\"\\u10e1\\u10d0\\u10e5\\u10d0\\u10e0\\u10d7\\u10d5\\u10d4\\u10da\\u10dd\"]"
+    assert_equal json, utf8.to_json(:ascii_only => true)
+    assert_equal utf8, parse(json)
+    assert_equal '["Ã"]', JSON.generate(["Ã"], :ascii_only => false)
+    assert_equal '["\\u00c3"]', JSON.generate(["Ã"], :ascii_only => true)
+    assert_equal ["€"], JSON.parse('["\u20ac"]')
+    utf8 = ["\xf0\xa0\x80\x81"]
+    json = "[\"\xf0\xa0\x80\x81\"]"
+    assert_equal json, JSON.generate(utf8, :ascii_only => false)
+    assert_equal utf8, JSON.parse(json)
+    json = '["\ud840\udc01"]'
+    assert_equal json, JSON.generate(utf8, :ascii_only => true)
+    assert_equal utf8, JSON.parse(json)
+  end
+
+  def test_chars
+    (0..0x7f).each do |i|
+      json = '["\u%04x"]' % i
+      if RUBY_VERSION >= "1.9."
+        i = i.chr
+      end
+      assert_equal i, JSON.parse(json).first[0]
+      if i == ?\b
+        generated = JSON.generate(["" << i])
+        assert '["\b"]' == generated || '["\10"]' == generated
+      elsif [?\n, ?\r, ?\t, ?\f].include?(i)
+        assert_equal '[' << ('' << i).dump << ']', JSON.generate(["" << i])
+      elsif i.chr < 0x20.chr
+        assert_equal json, JSON.generate(["" << i])
+      end
+    end
+    assert_raise(JSON::GeneratorError) do
+      JSON.generate(["\x80"], :ascii_only => true)
+    end
+    assert_equal "\302\200", JSON.parse('["\u0080"]').first
+  end
+end
-- 
cgit v1.2.3-70-g09d2