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