spotify 12.2.0 → 12.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/.gitignore +3 -1
  2. data/.rspec +5 -0
  3. data/CHANGELOG.md +45 -27
  4. data/Gemfile +4 -0
  5. data/README.markdown +52 -19
  6. data/Rakefile +16 -7
  7. data/lib/spotify.rb +109 -8
  8. data/lib/spotify/api.rb +61 -0
  9. data/lib/spotify/api/album.rb +14 -0
  10. data/lib/spotify/api/album_browse.rb +18 -0
  11. data/lib/spotify/api/artist.rb +10 -0
  12. data/lib/spotify/api/artist_browse.rb +23 -0
  13. data/lib/spotify/api/error.rb +6 -0
  14. data/lib/spotify/api/image.rb +16 -0
  15. data/lib/spotify/api/inbox.rb +9 -0
  16. data/lib/spotify/api/link.rb +25 -0
  17. data/lib/spotify/api/playlist.rb +39 -0
  18. data/lib/spotify/api/playlist_container.rb +23 -0
  19. data/lib/spotify/api/search.rb +27 -0
  20. data/lib/spotify/api/session.rb +46 -0
  21. data/lib/spotify/api/toplist_browse.rb +17 -0
  22. data/lib/spotify/api/track.rb +26 -0
  23. data/lib/spotify/api/user.rb +10 -0
  24. data/lib/spotify/defines.rb +109 -0
  25. data/lib/spotify/error.rb +62 -0
  26. data/lib/spotify/managed_pointer.rb +90 -0
  27. data/lib/spotify/objects.rb +16 -0
  28. data/lib/spotify/objects/album.rb +5 -0
  29. data/lib/spotify/objects/album_browse.rb +5 -0
  30. data/lib/spotify/objects/artist.rb +5 -0
  31. data/lib/spotify/objects/artist_browse.rb +5 -0
  32. data/lib/spotify/objects/image.rb +5 -0
  33. data/lib/spotify/objects/inbox.rb +5 -0
  34. data/lib/spotify/objects/link.rb +5 -0
  35. data/lib/spotify/objects/playlist.rb +5 -0
  36. data/lib/spotify/objects/playlist_container.rb +5 -0
  37. data/lib/spotify/objects/search.rb +5 -0
  38. data/lib/spotify/objects/session.rb +20 -0
  39. data/lib/spotify/objects/toplist_browse.rb +5 -0
  40. data/lib/spotify/objects/track.rb +5 -0
  41. data/lib/spotify/objects/user.rb +5 -0
  42. data/lib/spotify/structs.rb +46 -0
  43. data/lib/spotify/structs/audio_buffer_stats.rb +10 -0
  44. data/lib/spotify/structs/audio_format.rb +12 -0
  45. data/lib/spotify/structs/offline_sync_status.rb +24 -0
  46. data/lib/spotify/structs/playlist_callbacks.rb +32 -0
  47. data/lib/spotify/structs/playlist_container_callbacks.rb +14 -0
  48. data/lib/spotify/structs/session_callbacks.rb +48 -0
  49. data/lib/spotify/structs/session_config.rb +64 -0
  50. data/lib/spotify/structs/subscribers.rb +31 -0
  51. data/lib/spotify/types.rb +3 -0
  52. data/lib/spotify/types/image_id.rb +5 -0
  53. data/lib/spotify/types/nul_string.rb +51 -0
  54. data/lib/spotify/types/utf8_string.rb +24 -28
  55. data/lib/spotify/util.rb +36 -0
  56. data/lib/spotify/version.rb +7 -2
  57. data/spec/api-linux.xml +1887 -0
  58. data/spec/api-mac.xml +1886 -0
  59. data/spec/spec_helper.rb +20 -0
  60. data/spec/spotify/api_spec.rb +62 -0
  61. data/spec/spotify/defines_spec.rb +22 -0
  62. data/spec/spotify/enums_spec.rb +9 -0
  63. data/spec/spotify/managed_pointer_spec.rb +75 -0
  64. data/spec/spotify/spotify_spec.rb +69 -0
  65. data/spec/spotify/structs/session_config_spec.rb +20 -0
  66. data/spec/spotify/structs/struct_spec.rb +34 -0
  67. data/spec/spotify/structs/subscribers_spec.rb +31 -0
  68. data/spec/spotify/structs_spec.rb +19 -0
  69. data/spec/spotify/types/image_id_spec.rb +44 -0
  70. data/spec/spotify/types/nul_string_spec.rb +31 -0
  71. data/spec/spotify/types/utf8_string_spec.rb +24 -0
  72. data/spec/support/hook_spotify.rb +37 -0
  73. data/spec/support/spotify_util.rb +17 -0
  74. data/spotify.gemspec +6 -11
  75. metadata +99 -26
  76. data/lib/spotify/error_wrappers.rb +0 -165
  77. data/lib/spotify/functions.rb +0 -755
  78. data/lib/spotify/gc_wrappers.rb +0 -105
  79. data/lib/spotify/types/pointer.rb +0 -59
  80. data/spec/spotify_spec.rb +0 -467
@@ -1,105 +0,0 @@
1
- # This file contains a tiny (!) DSL that wraps existing Spotify
2
- # functions into versions that return Spotify::Pointers instead
3
- # of the usual FFI::Pointers. The Spotify::Pointer automatically
4
- # manages the underlying pointers reference count, which allows
5
- # us to piggyback on the Ruby GC mechanism.
6
-
7
- module Spotify
8
- # Wraps the function `function` so that it always returns
9
- # a Spotify::Pointer with correct refcount. Functions that
10
- # contain the word `create` are assumed to start out with
11
- # a refcount of `+1`.
12
- #
13
- # @note This method is removed at the bottom of this file.
14
- #
15
- # @param [#to_s] function
16
- # @param [#to_s] return_type
17
- # @raise [NoMethodError] if `function` is not defined
18
- # @see Spotify::Pointer
19
- def self.wrap_function(function, return_type)
20
- method(function) # make sure it exists
21
- define_singleton_method("#{function}!") do |*args, &block|
22
- pointer = public_send(function, *args, &block)
23
- Spotify::Pointer.new(pointer, return_type, function !~ /create/)
24
- end
25
- end
26
-
27
- # @macro [attach] wrap_function
28
- # Same as {Spotify}.`$1`, but wraps result in a {Spotify::Pointer}.
29
- #
30
- # @method $1!
31
- # @return [Spotify::Pointer<$2>]
32
- # @see #$1
33
- wrap_function :session_user, :user
34
- wrap_function :session_playlistcontainer, :playlistcontainer
35
- wrap_function :session_inbox_create, :playlist
36
- wrap_function :session_starred_create, :playlist
37
- wrap_function :session_starred_for_user_create, :playlist
38
- wrap_function :session_publishedcontainer_for_user_create, :playlistcontainer
39
-
40
- wrap_function :track_artist, :artist
41
- wrap_function :track_album, :album
42
- wrap_function :localtrack_create, :track
43
- wrap_function :track_get_playable, :track
44
-
45
- wrap_function :album_artist, :artist
46
-
47
- wrap_function :albumbrowse_create, :albumbrowse
48
- wrap_function :albumbrowse_album, :album
49
- wrap_function :albumbrowse_artist, :artist
50
- wrap_function :albumbrowse_track, :track
51
-
52
- wrap_function :artistbrowse_create, :artistbrowse
53
- wrap_function :artistbrowse_artist, :artist
54
- wrap_function :artistbrowse_track, :track
55
- wrap_function :artistbrowse_album, :album
56
- wrap_function :artistbrowse_similar_artist, :artist
57
- wrap_function :artistbrowse_tophit_track, :track
58
-
59
- wrap_function :image_create, :image
60
- wrap_function :image_create_from_link, :image
61
-
62
- wrap_function :link_as_track, :track
63
- wrap_function :link_as_track_and_offset, :track
64
- wrap_function :link_as_album, :album
65
- wrap_function :link_as_artist, :artist
66
- wrap_function :link_as_user, :user
67
-
68
- wrap_function :link_create_from_string, :link
69
- wrap_function :link_create_from_track, :link
70
- wrap_function :link_create_from_album, :link
71
- wrap_function :link_create_from_artist, :link
72
- wrap_function :link_create_from_search, :link
73
- wrap_function :link_create_from_playlist, :link
74
- wrap_function :link_create_from_artist_portrait, :link
75
- wrap_function :link_create_from_artistbrowse_portrait, :link
76
- wrap_function :link_create_from_album_cover, :link
77
- wrap_function :link_create_from_image, :link
78
- wrap_function :link_create_from_user, :link
79
-
80
- wrap_function :search_create, :search
81
- wrap_function :search_track, :track
82
- wrap_function :search_album, :album
83
- wrap_function :search_artist, :artist
84
- wrap_function :search_playlist, :playlist
85
-
86
- wrap_function :playlist_track, :track
87
- wrap_function :playlist_track_creator, :user
88
- wrap_function :playlist_owner, :user
89
- wrap_function :playlist_create, :playlist
90
-
91
- wrap_function :playlistcontainer_playlist, :playlist
92
- wrap_function :playlistcontainer_add_new_playlist, :playlist
93
- wrap_function :playlistcontainer_add_playlist, :playlist
94
- wrap_function :playlistcontainer_owner, :user
95
-
96
- wrap_function :toplistbrowse_create, :toplistbrowse
97
- wrap_function :toplistbrowse_artist, :artist
98
- wrap_function :toplistbrowse_album, :album
99
- wrap_function :toplistbrowse_track, :track
100
-
101
- wrap_function :inbox_post_tracks, :inbox
102
-
103
- # Clean up
104
- class << self; undef :wrap_function; end
105
- end
@@ -1,59 +0,0 @@
1
- module Spotify
2
- # The Pointer is a kind of AutoPointer specially tailored for Spotify
3
- # objects, that releases the raw pointer on GC.
4
- class Pointer < FFI::AutoPointer
5
- # Raised when #releaser_for is given an invalid type.
6
- class InvalidTypeError < StandardError
7
- end
8
-
9
- class << self
10
- # Create a proc that will accept a pointer of a given type and
11
- # release it with the correct function if it’s not null.
12
- #
13
- # @raise [InvalidTypeError] when given an invalid type
14
- # @param [Symbol] type
15
- # @return [Proc]
16
- def releaser_for(type)
17
- unless Spotify.respond_to?(:"#{type}_release!")
18
- raise InvalidTypeError, "#{type} is not a valid Spotify type"
19
- end
20
-
21
- lambda do |pointer|
22
- $stdout.puts "Spotify::#{type}_release!(#{pointer})" if $DEBUG
23
- Spotify.send(:"#{type}_release!", pointer) unless pointer.null?
24
- end
25
- end
26
-
27
- # Checks an object by pointer kind and type.
28
- #
29
- # @param [Object] object
30
- # @param [Symbol] type
31
- # @return [Boolean] true if object is a spotify pointer and of correct type
32
- def typechecks?(object, type)
33
- object.is_a?(Spotify::Pointer) && (object.type == type.to_s)
34
- end
35
- end
36
-
37
- # @return [Symbol] type
38
- attr_reader :type
39
-
40
- # Initialize a Spotify pointer, which will automatically decrease
41
- # the reference count of it’s pointer when garbage collected.
42
- #
43
- # @param [FFI::Pointer] pointer
44
- # @param [#to_s] type session, link, etc
45
- # @param [Boolean] add_ref will increase refcount by one if true
46
- def initialize(pointer, type, add_ref)
47
- super pointer, self.class.releaser_for(@type = type.to_s)
48
-
49
- unless pointer.null?
50
- Spotify.send(:"#{type}_add_ref!", pointer)
51
- end if add_ref
52
- end
53
-
54
- # @return [String] representation of the spotify pointer
55
- def to_s
56
- "<#{self.class} address=0x#{address.to_s(16)} type=#{type}>"
57
- end
58
- end
59
- end
@@ -1,467 +0,0 @@
1
- # coding: utf-8
2
- require 'rubygems' # needed for 1.8, does not matter in 1.9
3
-
4
- require 'ostruct'
5
- require 'set'
6
- require 'rbgccxml'
7
- require 'minitest/mock'
8
- require 'minitest/autorun'
9
-
10
- #
11
- # Hooking FFI for extra introspection
12
- #
13
- require 'ffi'
14
-
15
- module Spotify
16
- extend FFI::Library
17
- extend self
18
-
19
- def attach_function(name, func, arguments, returns = nil, options = nil)
20
- args = [name, func, arguments, returns, options].compact
21
- args.unshift name.to_s if func.is_a?(Array)
22
-
23
- hargs = [:name, :func, :args, :returns].zip args
24
- @attached_methods ||= {}
25
- @attached_methods[name.to_s] = hash = Hash[hargs]
26
-
27
- super
28
- end
29
-
30
- def resolve_type(type)
31
- type = find_type(type)
32
- type = type.type if type.respond_to?(:type)
33
- type
34
- end
35
-
36
- attr_reader :attached_methods
37
-
38
- RUBY_PLATFORM = ENV.fetch('RUBY_PLATFORM') do
39
- puts "[WARN] Tests running with default ruby platform, #{::RUBY_PLATFORM}, please be"
40
- puts "[WARN] specific in which platform to target by setting ENV[RUBY_PLATFORM]"
41
- puts "(warnings coming from #{__FILE__}:#{__LINE__})"
42
- puts
43
- ::RUBY_PLATFORM
44
- end
45
- end
46
-
47
- require 'spotify'
48
-
49
- module C
50
- extend FFI::Library
51
- ffi_lib 'C'
52
-
53
- typedef Spotify::UTF8String, :utf8_string
54
-
55
- attach_function :strncpy, [ :pointer, :utf8_string, :size_t ], :utf8_string
56
- end
57
-
58
- # Used for checking Spotify::Pointer things.
59
- module Spotify
60
- def bogus_add_ref!(pointer)
61
- end
62
-
63
- def bogus_release!(pointer)
64
- end
65
-
66
- # This may be called after our GC test. Randomly.
67
- def garbage_release!(pointer)
68
- end
69
- end
70
-
71
- #
72
- # Utility
73
- #
74
-
75
- API_H_PATH = File.expand_path("../api-#{Spotify.platform}.h", __FILE__)
76
- API_H_SRC = File.read(API_H_PATH)
77
- API_H_XML = RbGCCXML.parse(API_H_PATH)
78
-
79
- #
80
- # General
81
- #
82
- describe Spotify do
83
- describe "VERSION" do
84
- it "should be defined" do
85
- defined?(Spotify::VERSION).must_equal "constant"
86
- end
87
-
88
- it "should be the same version as in api.h" do
89
- spotify_version = API_H_SRC.match(/#define\s+SPOTIFY_API_VERSION\s+(\d+)/)[1]
90
- Spotify::API_VERSION.must_equal spotify_version.to_i
91
- end
92
- end
93
-
94
- describe ".enum_value!" do
95
- it "raises an error if given an invalid enum value" do
96
- proc { Spotify.enum_value!(:moo, "error value") }.must_raise(ArgumentError)
97
- end
98
-
99
- it "gives back the enum value for that enum" do
100
- Spotify.enum_value!(:ok, "error value").must_equal 0
101
- end
102
- end
103
-
104
- describe Spotify::SessionConfig do
105
- it "allows setting boolean values with bools" do
106
- subject = Spotify::SessionConfig.new
107
-
108
- subject[:compress_playlists].must_equal false
109
- subject[:dont_save_metadata_for_playlists].must_equal false
110
- subject[:initially_unload_playlists].must_equal false
111
-
112
- subject[:compress_playlists] = true
113
- subject[:dont_save_metadata_for_playlists] = true
114
- subject[:initially_unload_playlists] = true
115
-
116
- subject[:compress_playlists].must_equal true
117
- subject[:dont_save_metadata_for_playlists].must_equal true
118
- subject[:initially_unload_playlists].must_equal true
119
- end
120
-
121
- it "should be possible to set the callbacks" do
122
- subject = Spotify::SessionConfig.new
123
- subject[:callbacks] = Spotify::SessionCallbacks.new
124
- end
125
- end
126
-
127
- describe Spotify::OfflineSyncStatus do
128
- it "allows setting boolean values with bools" do
129
- subject = Spotify::OfflineSyncStatus.new
130
-
131
- subject[:syncing].must_equal false
132
- subject[:syncing] = true
133
- subject[:syncing].must_equal true
134
- end
135
- end
136
-
137
- describe "audio sample types" do
138
- # this is so we can just read audio frames easily based on the sample type
139
- Spotify.enum_type(:sampletype).symbols.each do |type|
140
- describe type do
141
- it "should have a corresponding FFI::Pointer#read_array_of_#{type}" do
142
- FFI::Pointer.new(1).must_respond_to "read_array_of_#{type}"
143
- end
144
- end
145
- end
146
- end
147
-
148
- describe "error wrapped functions" do
149
- wrapped_methods = Spotify.attached_methods.find_all { |meth, info| info[:returns] == :error }
150
- wrapped_methods.each do |meth, info|
151
- it "raises an error if #{meth}! returns non-ok" do
152
- Spotify.stub(meth, :bad_application_key) do
153
- proc { Spotify.send("#{meth}!") }.must_raise(Spotify::Error, /BAD_APPLICATION_KEY/)
154
- end
155
- end
156
- end
157
- end
158
-
159
- describe "GC wrapped functions" do
160
- gc_types = Set.new([:session, :track, :user, :playlistcontainer, :playlist, :link, :album, :artist, :search, :image, :albumbrowse, :artistbrowse, :toplistbrowse, :inbox])
161
- wrapped_methods = Spotify.attached_methods.find_all { |meth, info| gc_types.member?(info[:returns]) }
162
- wrapped_methods.each do |meth, info|
163
- it "returns a Spotify::Pointer for #{meth}!" do
164
- Spotify.stub(meth, lambda { FFI::Pointer.new(0) }) do
165
- Spotify.send("#{meth}!").must_be_instance_of Spotify::Pointer
166
- end
167
- end
168
- end
169
-
170
- it "adds a ref to the pointer if required" do
171
- session = FFI::Pointer.new(1)
172
- ref_added = false
173
-
174
- Spotify.stub(:session_user, FFI::Pointer.new(1)) do
175
- Spotify.stub(:user_add_ref, proc { ref_added = true; :ok }) do
176
- Spotify.session_user!(session)
177
- end
178
- end
179
-
180
- ref_added.must_equal true
181
- end
182
-
183
- it "does not add a ref when the result is null" do
184
- session = FFI::Pointer.new(1)
185
- ref_added = false
186
-
187
- Spotify.stub(:session_user, FFI::Pointer.new(0)) do
188
- Spotify.stub(:user_add_ref, proc { ref_added = true }) do
189
- Spotify.session_user!(session)
190
- end
191
- end
192
-
193
- ref_added.must_equal false
194
- end
195
-
196
- it "does not add a ref when the result already has one" do
197
- session = FFI::Pointer.new(1)
198
- ref_added = false
199
-
200
- Spotify.stub(:albumbrowse_create, FFI::Pointer.new(1)) do
201
- Spotify.stub(:albumbrowse_add_ref, proc { ref_added = true }) do
202
- # to avoid it trying to GC our fake pointer later, and cause a
203
- # segfault in our tests
204
- Spotify::Pointer.stub(:releaser_for, proc { proc {} }) do
205
- Spotify.albumbrowse_create!(session)
206
- end
207
- end
208
- end
209
-
210
- ref_added.must_equal false
211
- end
212
- end
213
-
214
- describe Spotify::Pointer do
215
- describe ".new" do
216
- it "adds a reference on the given pointer" do
217
- ref_added = false
218
-
219
- Spotify.stub(:bogus_add_ref!, proc { ref_added = true; :ok }) do
220
- Spotify::Pointer.new(FFI::Pointer.new(1), :bogus, true)
221
- end
222
-
223
- ref_added.must_equal true
224
- end
225
-
226
- it "does not add a reference on the given pointer if it is NULL" do
227
- ref_added = false
228
-
229
- Spotify.stub(:bogus_add_ref!, proc { ref_added = true; :ok }) do
230
- Spotify::Pointer.new(FFI::Pointer::NULL, :bogus, true)
231
- end
232
-
233
- ref_added.must_equal false
234
- end
235
-
236
- it "raises an error when given an invalid type" do
237
- proc { Spotify::Pointer.new(FFI::Pointer.new(1), :really_bogus, true) }.
238
- must_raise(Spotify::Pointer::InvalidTypeError, /invalid/)
239
- end
240
- end
241
-
242
- describe ".typechecks?" do
243
- it "typechecks a given spotify pointer" do
244
- pointer = Spotify::Pointer.new(FFI::Pointer.new(1), :bogus, true)
245
- bogus = OpenStruct.new(:type => :bogus)
246
- Spotify::Pointer.typechecks?(bogus, :bogus).must_equal false
247
- Spotify::Pointer.typechecks?(pointer, :link).must_equal false
248
- Spotify::Pointer.typechecks?(pointer, :bogus).must_equal true
249
- end
250
- end
251
-
252
- describe "garbage collection" do
253
- let(:my_pointer) { FFI::Pointer.new(1) }
254
-
255
- it "should work" do
256
- gc_count = 0
257
-
258
- Spotify.stub(:garbage_release!, proc { gc_count += 1 }) do
259
- 5.times { Spotify::Pointer.new(my_pointer, :garbage, false) }
260
- 5.times { GC.start; sleep 0.01 }
261
- end
262
-
263
- # GC tests are a bit funky, but as long as we garbage_release at least once, then
264
- # we can assume our GC works properly, but up the stakes just for the sake of it
265
- gc_count.must_be :>, 3
266
- end
267
- end
268
- end
269
-
270
- describe Spotify::UTF8String do
271
- let(:char) do
272
- char = "\xC4"
273
- char.force_encoding('ISO-8859-1') if char.respond_to?(:force_encoding)
274
- char
275
- end
276
-
277
- it "should convert any strings to UTF-8 before reading and writing" do
278
- dest = FFI::MemoryPointer.new(:char, 3) # two bytes for the ä, one for the NULL
279
- result = C.strncpy(dest, char, 3)
280
-
281
- result.encoding.must_equal Encoding::UTF_8
282
- result.must_equal "Ä"
283
- result.bytesize.must_equal 2
284
- end if "".respond_to?(:force_encoding)
285
-
286
- it "should do nothing if strings does not respond to #encode or #force_encoding" do
287
- dest = FFI::MemoryPointer.new(:char, 3) # two bytes for the ä, one for the NULL
288
- result = C.strncpy(dest, char, 3)
289
-
290
- result.must_equal "\xC4"
291
- result.bytesize.must_equal 1
292
- end unless "".respond_to?(:force_encoding)
293
- end
294
-
295
- describe Spotify::ImageID do
296
- let(:context) { nil }
297
- let(:subject) { Spotify.find_type(:image_id) }
298
- let(:null_pointer) { FFI::Pointer::NULL }
299
-
300
- let(:image_id_pointer) do
301
- pointer = FFI::MemoryPointer.new(:char, 20)
302
- pointer.write_string(image_id)
303
- pointer
304
- end
305
-
306
- let(:image_id) do
307
- # deliberate NULL in middle of string
308
- image_id = ":\xD94#\xAD\xD9\x97f\xE0\x00V6\x05\xC6\xE7n\xD2\xB0\xE4P"
309
- image_id.force_encoding("BINARY") if image_id.respond_to?(:force_encoding)
310
- image_id
311
- end
312
-
313
- describe "from_native" do
314
- it "should be nil given a null pointer" do
315
- subject.from_native(null_pointer, context).must_be_nil
316
- end
317
-
318
- it "should be an image id given a non-null pointer" do
319
- subject.from_native(image_id_pointer, context).must_equal image_id
320
- end
321
- end
322
-
323
- describe "to_native" do
324
- it "should be a null pointer given nil" do
325
- subject.to_native(nil, context).must_be_nil
326
- end
327
-
328
- it "should be a 20-byte C string given an actual string" do
329
- pointer = subject.to_native(image_id, context)
330
- pointer.read_string(20).must_equal image_id_pointer.read_string(20)
331
- end
332
-
333
- it "should raise an error given more or less than a 20 byte string" do
334
- proc { subject.to_native(image_id + image_id, context) }.must_raise ArgumentError
335
- proc { subject.to_native(image_id[0..10], context) }.must_raise ArgumentError
336
- end
337
- end
338
- end
339
- end
340
-
341
- describe "functions" do
342
- API_H_XML.functions.each do |func|
343
- next unless func["name"] =~ /\Asp_/
344
- attached_name = func["name"].sub(/\Asp_/, '')
345
-
346
- def type_of(type, return_type = false)
347
- return case type.to_cpp
348
- when "const char*"
349
- :utf8_string
350
- when /\A(::)?(char|int|size_t|bool|sp_scrobbling_state|sp_session\*|byte)\*/
351
- return_type ? :pointer : :buffer_out
352
- when /::(.+_cb)\*/
353
- $1.to_sym
354
- else :pointer
355
- end if type.is_a?(RbGCCXML::PointerType)
356
-
357
- case type["name"]
358
- when "unsigned int"
359
- :uint
360
- else
361
- type["name"].sub(/\Asp_/, '').to_sym
362
- end
363
- end
364
-
365
- describe func["name"] do
366
- it "should be attached" do
367
- Spotify.must_respond_to attached_name
368
- end
369
-
370
- it "should expect the correct number of arguments" do
371
- Spotify.attached_methods[attached_name][:args].count.
372
- must_equal func.arguments.count
373
- end
374
-
375
- it "should return the correct type" do
376
- current = Spotify.attached_methods[attached_name][:returns]
377
- actual = type_of(func.return_type, true)
378
-
379
- Spotify.resolve_type(current).must_equal Spotify.resolve_type(actual)
380
- end
381
-
382
- it "should expect the correct types of arguments" do
383
- current = Spotify.attached_methods[attached_name][:args]
384
- actual = func.arguments.map { |arg| type_of(arg.cpp_type) }
385
-
386
- current = current.map { |x| Spotify.resolve_type(x) }
387
- actual = actual.map { |x| Spotify.resolve_type(x) }
388
-
389
- current.must_equal actual
390
- end
391
- end
392
- end
393
- end
394
-
395
- describe "enums" do
396
- API_H_XML.enumerations.each do |enum|
397
- attached_enum = Spotify.enum_type enum["name"].sub(/\Asp_/, '').to_sym
398
- original_enum = enum.values.map { |v| [v["name"].downcase, v["init"]] }
399
-
400
- describe enum["name"] do
401
- it "should exist" do
402
- attached_enum.wont_be_nil
403
- end
404
-
405
- it "should match the definition" do
406
- attached_enum_map = attached_enum.symbol_map
407
- original_enum.each do |(name, value)|
408
- a_name, a_value = attached_enum_map.max_by { |(n, v)| (n.to_s.length if name.match(n.to_s)).to_i }
409
- attached_enum_map.delete(a_name)
410
- a_value.to_s.must_equal value
411
- end
412
- end
413
- end
414
- end
415
- end
416
-
417
- describe "structs" do
418
- API_H_XML.structs.each do |struct|
419
- next if struct["incomplete"]
420
-
421
- attached_struct = Spotify.constants.find do |const|
422
- struct["name"].gsub('_', '').match(/#{const}/i)
423
- end
424
-
425
- attached_members = Spotify.const_get(attached_struct).members.map(&:to_s)
426
-
427
- describe struct["name"] do
428
- it "should contain the same attributes" do
429
- struct.variables.map(&:name).each_with_index do |member, index|
430
- attached_members[index].must_equal member
431
- end
432
- end
433
- end
434
- end
435
-
436
- describe Spotify::Subscribers do
437
- it "should create the subscribers array using count" do
438
- # Memory looks like this:
439
- #
440
- # 00 00 00 00 <- count of subscribers
441
- # 00 00 00 00 <- pointer to subscriber 1
442
- # …… …… …… ……
443
- # 00 00 00 00 <- pointer to subscriber n
444
- real_struct = FFI::MemoryPointer.new(:char, 24)
445
- real_struct.put_uint(0, 2)
446
- subscribers = %w[a bb].map { |x| FFI::MemoryPointer.from_string(x) }
447
- real_struct.put_array_of_pointer(8, subscribers)
448
-
449
- struct = Spotify::Subscribers.new(real_struct)
450
- struct[:count].must_equal 2
451
- struct[:subscribers].size.must_equal 2
452
- struct[:subscribers][0].read_string.must_equal "a"
453
- struct[:subscribers][1].read_string.must_equal "bb"
454
- proc { struct[:subscribers][2] }.must_raise IndexError
455
- end
456
-
457
-
458
- it "should not fail given an empty subscribers struct" do
459
- subscribers = FFI::MemoryPointer.new(:uint)
460
- subscribers.write_uint(0)
461
-
462
- subject = Spotify::Subscribers.new(subscribers)
463
- subject[:count].must_equal 0
464
- proc { subject[:subscribers] }.must_raise ArgumentError
465
- end
466
- end
467
- end