#!/usr/bin/env ruby

require 'test/unit'
require 'json'

class TC_JSON < 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,
        'args'        => [ @a ],
      }.to_json(*args)
    end
  end

  def setup
    $KCODE = 'UTF8'
    @ary = [1, "foo", 3.14, 4711.0, 2.718, nil, [1,-2,3], false, true]
    @ary_to_parse = ["1", '"foo"', "3.14", "4711.0", "2.718", "null",
      "[1,-2,3]", "false", "true"]
    @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}'
    @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}'
  end

  def test_parse_value
    assert_equal("", parse('""'))
    assert_equal("\\", parse('"\\\\"'))
    assert_equal('"', parse('"\""'))
    assert_equal('\\"\\', parse('"\\\\\\"\\\\"'))
    assert_equal("\\a\"\b\f\n\r\t\0\037",
      parse('"\\a\"\b\f\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.E-3,null,[1,-2,3],false,true]'))
    assert_equal(@ary, parse(%Q{   [   1 , "foo"  ,  3.14 \t ,  47.11e+2 
      , 2718.E-3 ,\n null , [1, -2, 3 ], false , true\n ]  }))
  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

  def test_unparse
    json = unparse(@hash)
    assert_equal(@json2, json)
    parsed_json = parse(json)
    assert_equal(@hash, parsed_json)
    json = unparse({1=>2})
    assert_equal('{"1":2}', json)
    parsed_json = parse(json)
    assert_equal({"1"=>2}, parsed_json)
  end

  def test_parser_reset
    parser = Parser.new(@json)
    assert_equal(@hash, parser.parse)
    assert_equal(@hash, parser.parse)
  end

  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
    if JSON.support_unicode?
      utf8 = '© ≠ €!'
      json = '"\u00a9 \u2260 \u20ac!"'
      assert_equal json, utf8.to_json
      assert_equal utf8, parse(json)
      utf8 = "\343\201\202\343\201\204\343\201\206\343\201\210\343\201\212"
      json = '"\u3042\u3044\u3046\u3048\u304a"'
      assert_equal json, utf8.to_json
      assert_equal utf8, parse(json)
      utf8 = 'საქართველო'
      json = '"\u10e1\u10d0\u10e5\u10d0\u10e0\u10d7\u10d5\u10d4\u10da\u10dd"'
      assert_equal json, utf8.to_json
      assert_equal utf8, parse(json)
    else
      warn "No unicode support found - skipping test"
    end
  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_extended_json
    a = A.new(666)
    json = a.to_json
    a_again = JSON.parse(json)
    assert_kind_of a.class, a_again
    assert_equal a, a_again
  end

  def test_raw_strings
    raw = ''
    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
    json_raw = <<EOT.chomp
{\"json_class\":\"String\",\"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]}
EOT
# "
    assert_equal json_raw, json
    raw_again = JSON.parse(json)
    assert_equal raw, raw_again
  end

  def test_utf8_mode
    unless JSON.support_unicode?
      warn "No unicode support found - skipping test"
      return
    end
    $KCODE = 'NONE'
    utf8 = "© ≠ €! - \001"
    json = "\"© ≠ €! - \\u0001\""
    assert_equal json, utf8.to_json
    assert_equal utf8, parse(json)
    assert JSON.support_unicode?
    $KCODE = 'UTF8'
    utf8 = '© ≠ €!'
    json = '"\u00a9 \u2260 \u20ac!"'
    assert_equal json, utf8.to_json
    assert_equal utf8, parse(json)
    JSON.support_unicode = false
    assert !JSON.support_unicode?
    utf8 = "© ≠ €! - \001"
    json = "\"© ≠ €! - \\u0001\""
    assert_equal json, utf8.to_json
    assert_equal utf8, parse(json)
  end

  def test_backslash
    json = '"\\\\.(?i:gif|jpe?g|png)$"'
    data = JSON.parse(json)
    assert_equal json, JSON.unparse(data)
    json = '"\\""'
    data = JSON.parse(json)
    assert_equal json, JSON.unparse(data)
    json = '"\/"'
    data = JSON.parse(json)
    assert_equal '/', data
    assert_equal json, JSON.unparse(data)
  end
end
  # vim: set et sw=2 ts=2:
