describe "Spotify functions" do
  def type_of(type, return_type = false)
    return case type.to_cpp
      when "const char*"
        Spotify::UTF8String
      when /\A(::)?(char|int|size_t|bool|sp_scrobbling_state|sp_session\*|byte)\*/
        return_type ? :pointer : :buffer_out
      when /::(.+_cb)\*/
        $1.to_sym
      when /::(\w+)\*\*/
        :array
      when /::sp_(\w+)\*/
        const_name = $1.delete('_')
        real_const = Spotify.constants.find { |const| const =~ /#{const_name}\z/i }
        Spotify.const_get(real_const)
      else
        :pointer
    end if type.is_a?(RbGCCXML::PointerType)

    case type["name"]
    when "unsigned int"
      :uint
    when "sp_error"
      Spotify::APIError
    else
      type["name"].sub(/\Asp_/, '').to_sym
    end
  end

  API_H_XML.functions.each do |func|
    next unless func["name"] =~ /\Asp_/
    attached_name = func["name"].sub(/\Asp_/, '')

    # We test several things in this test because if we make the assertions
    # into separate tests there’s just too much noise on failure (e.g. when
    # additional functions are added, all assertions would fail)
    specify(func["name"]) do
      # it should be attached
      expect(Spotify).to respond_to attached_name

      # expect the correct number of arguments
      expect($attached_methods[attached_name][:args].count).
        to eq func.arguments.count

      # each argument has the right type
      current = $attached_methods[attached_name][:args]
      actual  = func.arguments.map { |arg| type_of(arg.cpp_type) }

      current = current.map { |x| Spotify.resolve_type(x) }
      actual  = actual.map  { |x| Spotify.resolve_type(x) }

      expect(current).to eq actual

      # returns the correct type
      current_type = $attached_methods[attached_name][:returns]
      actual_type  = type_of(func.return_type, true)

      # loosen restraints on some return types, we don’t have enough info from header file
      current_type = Spotify.resolve_type(current_type)
      actual_type  = Spotify.resolve_type(actual_type)

      if actual_type.is_a?(Class) and actual_type <= Spotify::ManagedPointer and attached_name !~ /create/
        expect(current_type).to eq actual_type::Retaining
      else
        expect(current_type).to eq actual_type
      end
    end
  end
end
