# encoding: UTF-8

# The rails tests set this to true. Both Rails and the JSON gem monkey patch the
# as_json methods on several base classes. Depending on which one replaces the
# method last the behavior will be different. Oj.mimic_JSON abides by the same
# conflicting behavior and the tests reflect that.
$rails_monkey = false unless defined?($rails_monkey)

class SharedMimicTest < Minitest::Test
  class Jam
    attr_accessor :x, :y

    def initialize(x, y)
      @x = x
      @y = y
    end

    def eql?(o)
      self.class == o.class && @x == o.x && @y == o.y
    end
    alias == eql?

    def as_json()
      {"json_class" => self.class.to_s, "x" => @x, "y" => @y}
    end

    def self.json_create(h)
      self.new(h['x'], h['y'])
    end

  end # Jam

  def setup
    @default_options = Oj.default_options
    @time = Time.at(1400000000).utc
    @expected_time_string =
      if defined?(Rails)
        %{"2014-05-13T16:53:20.000Z"}
      else
        %{"2014-05-13 16:53:20 UTC"}
      end
  end

  def teardown
    Oj.default_options = @default_options
  end

# exception
  def test_exception
    begin
      JSON.parse("{")
      puts "Failed"
    rescue JSON::ParserError
      assert(true)
    rescue Exception
      assert(false, 'Expected a JSON::ParserError')
    end
  end

# dump
  def test_dump_string
    json = JSON.dump([1, true, nil, @time])
    if $rails_monkey
      assert_equal(%{[1,true,null,#{@expected_time_string}]}, json)
    else
      assert_equal(%{[1,true,null,{"json_class":"Time","s":1400000000,"n":0}]}, json)
    end
  end

  def test_dump_with_options
    Oj.default_options= {:indent => 2} # JSON this will not change anything
    json = JSON.dump([1, true, nil, @time])
    if $rails_monkey
      assert_equal(%{[
  1,
  true,
  null,
  #{@expected_time_string}
]
}, json)
    else
      assert_equal(%{[
  1,
  true,
  null,
  {
    "json_class":"Time",
    "s":1400000000,
    "n\":0
  }
]
}, json)
    end
  end

  def test_dump_io
    s = StringIO.new()
    json = JSON.dump([1, true, nil, @time], s)
    assert_equal(s, json)
    if $rails_monkey
      assert_equal(%{[1,true,null,#{@expected_time_string}]}, s.string)
    else
      assert_equal(%{[1,true,null,{"json_class":"Time","s":1400000000,"n":0}]}, s.string)
    end
  end
  # TBD options

  def test_dump_struct
    # anonymous Struct not supported by json so name it
    if Object.const_defined?("Struct::Abc")
      s = Struct::Abc
    else
      s = Struct.new("Abc", :a, :b, :c)
    end
    o = s.new(1, 'two', [true, false])
    json = JSON.dump(o)
    if o.respond_to?(:as_json)
      if $rails_monkey
        assert_equal(%|{"a":1,"b":"two","c":[true,false]}|, json)
      else
        assert_equal(%|{"json_class":"Struct::Abc","v":[1,"two",[true,false]]}|, json)
      end
    else
      j = '"' + o.to_s.gsub('"', '\\"') + '"'
      assert_equal(j, json)
    end
  end

# load
  def test_load_string
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON.load(json)
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
  end

  def test_load_io
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON.load(StringIO.new(json))
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
  end

  def test_load_proc
    Oj.mimic_JSON
    children = []
    json = %{{"a":1,"b":[true,false]}}
    if 'rubinius' == $ruby
      obj = JSON.load(json) {|x| children << x }
    else
      p = Proc.new {|x| children << x }
      obj = JSON.load(json, p)
    end
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
    assert([1, true, false, [true, false], { 'a' => 1, 'b' => [true, false]}] == children ||
           [true, false, [true, false], 1, { 'a' => 1, 'b' => [true, false]}] == children,
           "children don't match")
  end

  def test_parse_with_quirks_mode
    json = %{null}
    assert_equal(nil, JSON.parse(json, :quirks_mode => true))
    assert_raises(JSON::ParserError) { JSON.parse(json, :quirks_mode => false) }
    assert_raises(JSON::ParserError) { JSON.parse(json) }
  end

  def test_parse_with_empty_string
    Oj.mimic_JSON
    assert_raises(JSON::ParserError) { JSON.parse(' ') }
    assert_raises(JSON::ParserError) { JSON.parse("\t\t\n   ") }
  end

# []
  def test_bracket_load
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON[json]
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
  end

  def test_bracket_dump
    json = JSON[[1, true, nil]]
    assert_equal(%{[1,true,null]}, json)
  end

# generate
  def test_generate
    json = JSON.generate({ 'a' => 1, 'b' => [true, false]})
    assert(%{{"a":1,"b":[true,false]}} == json ||
           %{{"b":[true,false],"a":1}} == json)
  end
  def test_generate_options
    json = JSON.generate({ 'a' => 1, 'b' => [true, false]},
                         :indent => "--",
                         :array_nl => "\n",
                         :object_nl => "#\n",
                         :space => "*",
                         :space_before => "~")
    assert(%{{#
--"a"~:*1,#
--"b"~:*[
----true,
----false
--]#
}} == json ||
%{{#
--"b"~:*[
----true,
----false
--],#
--"a"~:*1#
}} == json)
  end

# fast_generate
  def test_fast_generate
    json = JSON.generate({ 'a' => 1, 'b' => [true, false]})
    assert(%{{"a":1,"b":[true,false]}} == json ||
           %{{"b":[true,false],"a":1}} == json)
  end

# pretty_generate
  def test_pretty_generate
    json = JSON.pretty_generate({ 'a' => 1, 'b' => [true, false]})
    assert(%{{
  "a": 1,
  "b": [
    true,
    false
  ]
}} == json ||
%{{
  "b": [
    true,
    false
  ],
  "a": 1
}} == json)
  end

# parse
  def test_parse
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON.parse(json)
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
  end
  def test_parse_sym_names
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON.parse(json, :symbolize_names => true)
    assert_equal({ :a => 1, :b => [true, false]}, obj)
  end
  def test_parse_additions
    jam = Jam.new(true, 58)
    json = Oj.dump(jam, :mode => :compat, :use_to_json => true)
    obj = JSON.parse(json)
    assert_equal(jam, obj)
    obj = JSON.parse(json, :create_additions => true)
    assert_equal(jam, obj)
    obj = JSON.parse(json, :create_additions => false)
    assert_equal({'json_class' => 'SharedMimicTest::Jam', 'x' => true, 'y' => 58}, obj)
    json.gsub!('json_class', 'kson_class')
    JSON.create_id = 'kson_class'
    obj = JSON.parse(json, :create_additions => true)
    JSON.create_id = 'json_class'
    assert_equal(jam, obj)

    assert_nothing_raised { JSON.create_id = nil }
  end
  def test_parse_bang
    json = %{{"a":1,"b":[true,false]}}
    obj = JSON.parse!(json)
    assert_equal({ 'a' => 1, 'b' => [true, false]}, obj)
  end

# recurse_proc
  def test_recurse_proc
    children = []
    JSON.recurse_proc({ 'a' => 1, 'b' => [true, false]}) { |x| children << x }
    # JRuby 1.7.0 rb_yield() is broken and converts the [true, false] array into true
    unless 'jruby' == $ruby
      assert([1, true, false, [true, false], { 'a' => 1, 'b' => [true, false]}] == children ||
             [true, false, [true, false], 1, { 'b' => [true, false], 'a' => 1}] == children)
    end
  end

# make sure to_json is defined for object.
  def test_mimic_to_json
    {'a' => 1}.to_json()
    Object.new().to_json()
  end
end # SharedMimicTest

if defined?(ActiveSupport)
  class SharedMimicRailsTest < SharedMimicTest
    def test_activesupport_exception
      begin
        ActiveSupport::JSON.decode("{")
        puts "Failed"
      rescue ActiveSupport::JSON.parse_error
        assert(true)
      rescue Exception
        assert(false, 'Expected a JSON::ParserError')
      end
    end

    def test_activesupport_encode
      Oj.default_options= {:indent => 0}
      json = ActiveSupport::JSON.encode([1, true, nil])
      assert_equal(%{[1,true,null]}, json)
    end
  end # SharedMimicRailsTest
end
