require 'gir_ffi_test_helper'

describe GLib::Array do
  it "knows its element type" do
    arr = GLib::Array.new :gint32
    assert_equal :gint32, arr.element_type
  end

  describe "#append_vals" do
    before do
      @arr = GLib::Array.new :gint32
      @result = @arr.append_vals [1, 2, 3]
    end

    it "appends values" do
      assert_equal 3, @arr.len
    end

    it "returns self" do
      assert_equal @result, @arr
    end
  end

  describe "#each" do
    before do
      @arr = GLib::Array.new(:gint32).append_vals [1, 2, 3]
    end

    it "iterates over the values" do
      a = []
      @arr.each {|v| a << v }

      assert_equal [1, 2, 3], a
    end
  end

  describe "::wrap" do
    it "wraps a pointer, taking the element type as the first argument" do
      arr = GLib::Array.new :gint32
      arr.append_vals [1, 2, 3]
      arr2 = GLib::Array.wrap :gint32, arr.to_ptr
      assert_equal arr.to_a, arr2.to_a
    end

    it "warns the element sizes don't match" do
      arr = GLib::Array.new :gint32
      arr.append_vals [1, 2, 3]
      proc { GLib::Array.wrap :gint8, arr.to_ptr }.must_output nil, /sizes do not match/
    end

    it "handles a struct as the element type" do
      vals = [1, 2, 3].map {|i| GObject::EnumValue.new.tap {|ev| ev.value = i} }
      arr = GLib::Array.new GObject::EnumValue
      arr.append_vals vals
      arr2 = GLib::Array.wrap GObject::EnumValue, arr.to_ptr
      arr2.to_a.must_equal arr.to_a
    end
  end

  it "includes Enumerable" do
    GLib::Array.must_include Enumerable
  end

  it "has a working #to_a method" do
    arr = GLib::Array.new :gint32
    arr.append_vals [1, 2, 3]
    assert_equal [1, 2, 3], arr.to_a
  end

  describe ".from" do
    it "creates a GArray from an array of :gint32" do
      arr = GLib::Array.from :gint32, [3, 2, 1]
      arr.data.read_array_of_int32(3).must_equal [3, 2, 1]
    end

    it "creates a GArray from an array of :gboolean" do
      arr = GLib::Array.from :gboolean, [true, false, true]
      arr.data.read_array_of_int(3).must_equal [1, 0, 1]
    end

    it "return its argument if given a GArray" do
      arr = GLib::Array.new :gint32
      arr.append_vals [3, 2, 1]
      arr2 = GLib::Array.from :foo, arr
      assert arr2.equal? arr
    end

    it "wraps its argument if given a pointer" do
      arr = GLib::Array.new :gint32
      arr.append_vals [3, 2, 1]
      pointer = arr.to_ptr
      assert_instance_of FFI::Pointer, pointer
      arr2 = GLib::Array.from :gint32, pointer
      assert_instance_of GLib::Array, arr2
      refute arr2.equal? arr
      arr2.to_a.must_equal arr.to_a
    end
  end

  describe "#==" do
    it "returns true when comparing to an array with the same elements" do
      arr = GLib::Array.from :gint32, [1, 2, 3]

      arr.must_be :==, [1, 2, 3]
    end

    it "returns false when comparing to an array with different elements" do
      arr = GLib::Array.from :gint32, [1, 2, 3]

      arr.wont_be :==, [1, 2]
    end

    it "returns true when comparing to a GArray with the same elements" do
      arr = GLib::Array.from :gint32, [1, 2, 3]
      other = GLib::Array.from :gint32, [1, 2, 3]

      arr.must_be :==, other
    end

    it "returns false when comparing to a GArray with different elements" do
      arr = GLib::Array.from :gint32, [1, 2, 3]
      other = GLib::Array.from :gint32, [1, 2]

      arr.wont_be :==, other
    end
  end

  describe "#index" do
    it "returns the proper element for an array of :gint32" do
      arr = GLib::Array.from :gint32, [1, 2, 3]
      arr.index(2).must_equal 3
    end

    it "returns the proper element for an array of :utf8" do
      arr = GLib::Array.from :utf8, ["a", "b", "c"]
      arr.index(1).must_equal "b"
    end

    it "returns the proper element for an array of :gboolean" do
      arr = GLib::Array.from :gboolean, [true, false, true]
      arr.index(1).must_equal false
    end

    it "returns the proper element for an array of struct" do
      vals = [1, 2, 3].map {|i| GObject::EnumValue.new.tap {|ev| ev.value = i} }
      arr = GLib::Array.from GObject::EnumValue, vals
      arr.index(1).value.must_equal 2
    end

    it "raises an error if the index is out of bounds" do
      arr = GLib::Array.from :gint32, [1, 2, 3]
      lambda { arr.index(16) }.must_raise IndexError
      lambda { arr.index(-1) }.must_raise IndexError
    end
  end
end
