spec/spotify_spec.rb in spotify-12.0.0 vs spec/spotify_spec.rb in spotify-12.0.1
- old
+ new
@@ -1,9 +1,12 @@
# coding: utf-8
require 'rubygems' # needed for 1.8, does not matter in 1.9
+require 'ostruct'
+require 'set'
require 'rbgccxml'
+require 'minitest/mock'
require 'minitest/autorun'
#
# Hooking FFI for extra introspection
#
@@ -42,10 +45,23 @@
typedef Spotify::UTF8String, :utf8_string
attach_function :strncpy, [ :pointer, :utf8_string, :size_t ], :utf8_string
end
+# Used for checking Spotify::Pointer things.
+module Spotify
+ def bogus_add_ref(pointer)
+ end
+
+ def bogus_release(pointer)
+ end
+
+ # This may be called after our GC test. Randomly.
+ def garbage_release(pointer)
+ end
+end
+
#
# Utility
#
API_H_PATH = File.expand_path('../api.h', __FILE__)
@@ -65,10 +81,20 @@
spotify_version = API_H_SRC.match(/#define\s+SPOTIFY_API_VERSION\s+(\d+)/)[1]
Spotify::API_VERSION.must_equal spotify_version.to_i
end
end
+ describe ".enum_value!" do
+ it "raises an error if given an invalid enum value" do
+ proc { Spotify.enum_value!(:moo, "error value") }.must_raise(ArgumentError)
+ end
+
+ it "gives back the enum value for that enum" do
+ Spotify.enum_value!(:ok, "error value").must_equal 0
+ end
+ end
+
describe Spotify::SessionConfig do
it "allows setting boolean values with bools" do
subject = Spotify::SessionConfig.new
subject[:compress_playlists].must_equal false
@@ -109,11 +135,133 @@
end
end
end
end
- describe "UTF8 string" do
+ describe "error wrapped functions" do
+ wrapped_methods = Spotify.attached_methods.find_all { |meth, info| info[:returns] == :error }
+ wrapped_methods.each do |meth, info|
+ it "raises an error if #{meth}! returns non-ok" do
+ Spotify.stub(meth, :bad_application_key) do
+ proc { Spotify.send("#{meth}!") }.must_raise(Spotify::Error, /BAD_APPLICATION_KEY/)
+ end
+ end
+ end
+ end
+
+ describe "GC wrapped functions" do
+ gc_types = Set.new([:session, :track, :user, :playlistcontainer, :playlist, :link, :album, :artist, :search, :image, :albumbrowse, :artistbrowse, :toplistbrowse, :inbox])
+ wrapped_methods = Spotify.attached_methods.find_all { |meth, info| gc_types.member?(info[:returns]) }
+ wrapped_methods.each do |meth, info|
+ it "returns a Spotify::Pointer for #{meth}!" do
+ Spotify.stub(meth, lambda { FFI::Pointer.new(0) }) do
+ Spotify.send("#{meth}!").must_be_instance_of Spotify::Pointer
+ end
+ end
+ end
+
+ it "adds a ref to the pointer if required" do
+ session = FFI::Pointer.new(1)
+ ref_added = false
+
+ Spotify.stub(:session_user, FFI::Pointer.new(1)) do
+ Spotify.stub(:user_add_ref, proc { ref_added = true }) do
+ Spotify.session_user!(session)
+ end
+ end
+
+ ref_added.must_equal true
+ end
+
+ it "does not add a ref when the result is null" do
+ session = FFI::Pointer.new(1)
+ ref_added = false
+
+ Spotify.stub(:session_user, FFI::Pointer.new(0)) do
+ Spotify.stub(:user_add_ref, proc { ref_added = true }) do
+ Spotify.session_user!(session)
+ end
+ end
+
+ ref_added.must_equal false
+ end
+
+ it "does not add a ref when the result already has one" do
+ session = FFI::Pointer.new(1)
+ ref_added = false
+
+ Spotify.stub(:albumbrowse_create, FFI::Pointer.new(1)) do
+ Spotify.stub(:albumbrowse_add_ref, proc { ref_added = true }) do
+ # to avoid it trying to GC our fake pointer later, and cause a
+ # segfault in our tests
+ Spotify::Pointer.stub(:releaser_for, proc { proc {} }) do
+ Spotify.albumbrowse_create!(session)
+ end
+ end
+ end
+
+ ref_added.must_equal false
+ end
+ end
+
+ describe Spotify::Pointer do
+ describe ".new" do
+ it "adds a reference on the given pointer" do
+ ref_added = false
+
+ Spotify.stub(:bogus_add_ref, proc { ref_added = true }) do
+ Spotify::Pointer.new(FFI::Pointer.new(1), :bogus, true)
+ end
+
+ ref_added.must_equal true
+ end
+
+ it "does not add a reference on the given pointer if it is NULL" do
+ ref_added = false
+
+ Spotify.stub(:bogus_add_ref, proc { ref_added = true }) do
+ Spotify::Pointer.new(FFI::Pointer::NULL, :bogus, true)
+ end
+
+ ref_added.must_equal false
+ end
+
+ it "raises an error when given an invalid type" do
+ proc { Spotify::Pointer.new(FFI::Pointer.new(1), :really_bogus, true) }.
+ must_raise(Spotify::Pointer::InvalidTypeError, /invalid/)
+ end
+ end
+
+ describe ".typechecks?" do
+ it "typechecks a given spotify pointer" do
+ pointer = Spotify::Pointer.new(FFI::Pointer.new(1), :bogus, true)
+ bogus = OpenStruct.new(:type => :bogus)
+ Spotify::Pointer.typechecks?(bogus, :bogus).must_equal false
+ Spotify::Pointer.typechecks?(pointer, :link).must_equal false
+ Spotify::Pointer.typechecks?(pointer, :bogus).must_equal true
+ end
+ end
+
+ describe "garbage collection" do
+ let(:my_pointer) { FFI::Pointer.new(1) }
+
+ it "should work" do
+ gc_count = 0
+
+ Spotify.stub(:garbage_release, proc { gc_count += 1 }) do
+ 5.times { Spotify::Pointer.new(my_pointer, :garbage, false) }
+ 5.times { GC.start; sleep 0.01 }
+ end
+
+ # GC tests are a bit funky, but as long as we garbage_release at least once, then
+ # we can assume our GC works properly, but up the stakes just for the sake of it
+ gc_count.must_be :>, 3
+ end
+ end
+ end
+
+ describe Spotify::UTF8String do
let(:char) do
char = "\xC4"
char.force_encoding('ISO-8859-1') if char.respond_to?(:force_encoding)
char
end
@@ -134,10 +282,10 @@
result.must_equal "\xC4"
result.bytesize.must_equal 1
end unless "".respond_to?(:force_encoding)
end
- describe "Image ID" do
+ describe Spotify::ImageID do
let(:context) { nil }
let(:subject) { Spotify.find_type(:image_id) }
let(:null_pointer) { FFI::Pointer::NULL }
let(:image_id_pointer) do