spotify 12.2.0 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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