hallon 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGELOG.md +40 -0
  2. data/README.markdown +1 -1
  3. data/dev/application_key_converter.rb +11 -0
  4. data/examples/example_support.rb +4 -0
  5. data/examples/playing_audio.rb +1 -1
  6. data/lib/hallon.rb +11 -0
  7. data/lib/hallon/album_browse.rb +3 -3
  8. data/lib/hallon/artist_browse.rb +20 -4
  9. data/lib/hallon/blob.rb +11 -0
  10. data/lib/hallon/enumerator.rb +5 -0
  11. data/lib/hallon/ext/spotify.rb +2 -0
  12. data/lib/hallon/image.rb +7 -1
  13. data/lib/hallon/link.rb +5 -2
  14. data/lib/hallon/observable.rb +48 -1
  15. data/lib/hallon/player.rb +15 -26
  16. data/lib/hallon/playlist.rb +16 -25
  17. data/lib/hallon/playlist_container.rb +38 -0
  18. data/lib/hallon/search.rb +75 -4
  19. data/lib/hallon/session.rb +31 -42
  20. data/lib/hallon/toplist.rb +3 -3
  21. data/lib/hallon/track.rb +28 -10
  22. data/lib/hallon/version.rb +1 -1
  23. data/spec/hallon/album_browse_spec.rb +68 -18
  24. data/spec/hallon/album_spec.rb +62 -27
  25. data/spec/hallon/artist_browse_spec.rb +106 -31
  26. data/spec/hallon/artist_spec.rb +32 -18
  27. data/spec/hallon/blob_spec.rb +6 -0
  28. data/spec/hallon/enumerator_spec.rb +10 -0
  29. data/spec/hallon/error_spec.rb +4 -4
  30. data/spec/hallon/hallon_spec.rb +1 -1
  31. data/spec/hallon/image_spec.rb +58 -47
  32. data/spec/hallon/link_spec.rb +51 -43
  33. data/spec/hallon/observable/album_browse_spec.rb +1 -1
  34. data/spec/hallon/observable/artist_browse_spec.rb +1 -1
  35. data/spec/hallon/observable/image_spec.rb +1 -1
  36. data/spec/hallon/observable/playlist_container_spec.rb +4 -4
  37. data/spec/hallon/observable/playlist_spec.rb +14 -14
  38. data/spec/hallon/observable/post_spec.rb +1 -1
  39. data/spec/hallon/observable/search_spec.rb +1 -1
  40. data/spec/hallon/observable/session_spec.rb +17 -17
  41. data/spec/hallon/observable/toplist_spec.rb +1 -1
  42. data/spec/hallon/observable_spec.rb +40 -6
  43. data/spec/hallon/player_spec.rb +1 -1
  44. data/spec/hallon/playlist_container_spec.rb +96 -13
  45. data/spec/hallon/playlist_spec.rb +180 -45
  46. data/spec/hallon/search_spec.rb +211 -28
  47. data/spec/hallon/session_spec.rb +44 -38
  48. data/spec/hallon/toplist_spec.rb +31 -14
  49. data/spec/hallon/track_spec.rb +159 -50
  50. data/spec/hallon/user_post_spec.rb +10 -5
  51. data/spec/hallon/user_spec.rb +60 -50
  52. data/spec/spec_helper.rb +40 -15
  53. data/spec/support/album_mocks.rb +30 -0
  54. data/spec/support/artist_mocks.rb +36 -0
  55. data/spec/support/common_objects.rb +0 -201
  56. data/spec/support/image_mocks.rb +34 -0
  57. data/spec/support/playlist_container_mocks.rb +36 -0
  58. data/spec/support/playlist_mocks.rb +70 -0
  59. data/spec/support/search_mocks.rb +23 -0
  60. data/spec/support/session_mocks.rb +33 -0
  61. data/spec/support/toplist_mocks.rb +19 -0
  62. data/spec/support/track_mocks.rb +28 -0
  63. data/spec/support/user_mocks.rb +20 -0
  64. metadata +40 -18
  65. data/spec/support/context_stub_session.rb +0 -5
@@ -1,70 +1,154 @@
1
1
  # coding: utf-8
2
2
  describe Hallon::Track do
3
- it { should be_a Hallon::Loadable }
3
+ let(:track) do
4
+ Hallon::Track.new(mock_tracks[:default])
5
+ end
6
+
7
+ let(:empty_track) do
8
+ Hallon::Track.new(mock_tracks[:empty])
9
+ end
10
+
11
+ let(:autolinked_track) do
12
+ Hallon::Track.new(mock_tracks[:linked])
13
+ end
14
+
15
+ specify { track.should be_a Hallon::Loadable }
4
16
 
5
17
  it_should_behave_like "a Linkable object" do
6
18
  let(:spotify_uri) { "spotify:track:7N2Vc8u56VGA4KUrGbikC2#01:00" }
7
19
  end
8
20
 
9
- let(:track) { Hallon::Track.new(mock_track) }
10
- subject { track }
21
+ describe "#loaded?" do
22
+ it "returns true if the track is loaded" do
23
+ track.should be_loaded
24
+ end
25
+ end
11
26
 
12
- it { should be_loaded }
13
- its(:name) { should eq "They" }
14
- its(:disc) { should be 2 }
15
- its(:index) { should be 7 }
16
- its(:status) { should be :ok }
17
- its(:duration) { should eq 123.456 }
18
- its(:popularity) { should eq 0.42 }
19
- its(:album) { should eq Hallon::Album.new(mock_album) }
20
- its(:artist) { should eq Hallon::Artist.new(mock_artist) }
21
- its('artists.size') { should eq 2 }
22
- its('artists.to_a') { should eq [mock_artist, mock_artist_two].map{ |p| Hallon::Artist.new(p) } }
27
+ describe "#name" do
28
+ it "returns the track’s name" do
29
+ track.name.should eq "They"
30
+ end
23
31
 
24
- describe "#starred=" do
25
- around { |test| mock_session(&test) }
32
+ it "returns an empty string if the track’s name is unavailable" do
33
+ empty_track.name.should be_empty
34
+ end
35
+ end
26
36
 
27
- it "should delegate to session to unstar" do
28
- session.should_receive(:unstar).with(track)
29
- track.starred = false
37
+ describe "#disc" do
38
+ it "returns the track’s disc number in it’s album" do
39
+ track.disc.should eq 2
30
40
  end
41
+ end
31
42
 
32
- it "should delegate to session to star" do
33
- session.should_receive(:star).with(track)
34
- track.starred = true
43
+ describe "#index" do
44
+ it "returns the track’s position on the disc" do
45
+ track.index.should eq 7
46
+ end
47
+ end
48
+
49
+ describe "#status" do
50
+ it "returns the track’s status" do
51
+ track.status.should eq :ok
35
52
  end
53
+ end
36
54
 
37
- it "should change starred status of track" do
38
- track.should be_starred
39
- track.starred = false
40
- track.should_not be_starred
55
+ describe "#duration" do
56
+ it "returns track’s duration" do
57
+ track.duration.should eq 123.456
58
+ end
59
+ end
60
+
61
+ describe "#popularity" do
62
+ it "returns the track’s popularity" do
63
+ track.popularity.should eq 42
41
64
  end
42
65
  end
43
66
 
44
- describe "session bound queries" do
45
- subject { Hallon::Track.new(mock_track) }
46
- around { |test| mock_session(&test) }
67
+ describe "#playable_track" do
68
+ it "returns the autolinked track" do
69
+ linked = autolinked_track.playable_track
70
+ linked.should_not eq autolinked_track
71
+ linked.should eq track
72
+ end
73
+
74
+ it "returns itself if the track is not autolinked" do
75
+ track.playable_track.should eq track
76
+ end
77
+ end
78
+
79
+ describe "#available?" do
80
+ it "returns true if the track is available for playback" do
81
+ track.should be_available
82
+ end
83
+ end
84
+
85
+ describe "#local?" do
86
+ it "returns true if the track is a local track" do
87
+ track.should_not be_local
88
+ end
89
+ end
47
90
 
48
- it { should be_available }
49
- it { should_not be_local }
50
- it { should be_autolinked }
51
- it { should be_starred }
91
+ describe "#autolinked?" do
92
+ it "returns true if the track is autolinked to another for playback" do
93
+ track.should be_autolinked
94
+ end
95
+ end
52
96
 
53
- its(:availability) { should eq :available }
97
+ describe "#availability" do
98
+ it "returns the track’s availability" do
99
+ track.availability.should eq :available
100
+ end
54
101
  end
55
102
 
56
- describe "album" do
57
- it "should be an album when there is one" do
103
+ describe "#album" do
104
+ it "returns the track’s album" do
58
105
  track.album.should eq Hallon::Album.new(mock_album)
59
106
  end
60
107
 
61
- it "should be nil when there isnt one" do
62
- Spotify.should_receive(:track_album).and_return(null_pointer)
63
- track.album.should be_nil
108
+ it "returns nil if the tracks not loaded" do
109
+ empty_track.album.should be_nil
110
+ end
111
+ end
112
+
113
+ describe "#artist" do
114
+ it "returns the track’s artist" do
115
+ track.artist.should eq Hallon::Artist.new(mock_artist)
116
+ end
117
+
118
+ it "returns nil if the track’s not loaded" do
119
+ empty_track.artist.should be_nil
120
+ end
121
+ end
122
+
123
+ describe "#artists" do
124
+ it "returns an enumerator of the track’s artists" do
125
+ track.artists.to_a.should eq instantiate(Hallon::Artist, mock_artist, mock_artist_two)
126
+ end
127
+
128
+ it "returns an empty enumerator if the track has no artists" do
129
+ empty_track.artists.should be_empty
130
+ end
131
+ end
132
+
133
+ describe "#starred=" do
134
+ it "should delegate to session to unstar" do
135
+ session.should_receive(:unstar).with(track)
136
+ track.starred = false
137
+ end
138
+
139
+ it "should delegate to session to star" do
140
+ session.should_receive(:star).with(track)
141
+ track.starred = true
142
+ end
143
+
144
+ it "should change starred status of track" do
145
+ track.should be_starred
146
+ track.starred = false
147
+ track.should_not be_starred
64
148
  end
65
149
  end
66
150
 
67
- describe "to_link" do
151
+ describe "#to_link" do
68
152
  it "should pass the current offset by default" do
69
153
  track.should_receive(:offset).and_return(10)
70
154
  track.to_link.to_str.should match(/#00:10/)
@@ -91,9 +175,14 @@ describe Hallon::Track do
91
175
  let(:artist) { Spotify.link_create_from_string!('spotify:artist:3bftcFwl4vqRNNORRsqm1G') }
92
176
  let(:album) { Spotify.link_create_from_string!('spotify:album:1xvnWMz2PNFf7mXOSRuLws') }
93
177
 
178
+ it "does nothing if the track is not a placeholder" do
179
+ track.stub(:placeholder? => false)
180
+ track.unwrap.should eq track
181
+ end
182
+
94
183
  it "should unwrap a playlist placeholder into a playlist" do
95
184
  Spotify.should_receive(:link_create_from_track!).and_return(playlist)
96
- mock_session { track.unwrap.should eq Hallon::Playlist.new(playlist) }
185
+ track.unwrap.should eq Hallon::Playlist.new(playlist)
97
186
  end
98
187
 
99
188
  it "should unwrap an album placeholder into an album" do
@@ -113,7 +202,7 @@ describe Hallon::Track do
113
202
  end
114
203
  end
115
204
 
116
- describe "offset" do
205
+ describe "#offset" do
117
206
  let(:without_offset) { 'spotify:track:7N2Vc8u56VGA4KUrGbikC2' }
118
207
  let(:with_offset) { without_offset + '#1:00' }
119
208
 
@@ -131,18 +220,38 @@ describe Hallon::Track do
131
220
  end
132
221
 
133
222
  describe "a local track" do
134
- subject do
223
+ let(:local) do
135
224
  Hallon::Track.local "Nissy", "Emmy", "Coolio", 100
136
225
  end
137
226
 
138
- its(:name) { should eq "Nissy" }
139
- its("album.name") { should eq "Coolio" }
140
- its("artist.name") { should eq "Emmy" }
141
- its(:duration) { should eq 0.1 }
227
+ describe "#name" do
228
+ it "returns the track’s name" do
229
+ local.name.should eq "Nissy"
230
+ end
231
+ end
232
+
233
+ describe "#album" do
234
+ it "returns the track’s album" do
235
+ local.album.name.should eq "Coolio"
236
+ end
237
+ end
238
+
239
+ describe "#artist" do
240
+ it "returns the track’s artist" do
241
+ local.artist.name.should eq "Emmy"
242
+ end
243
+ end
244
+
245
+ describe "#duration" do
246
+ it "returns the track’s duration" do
247
+ local.duration.should eq 0.1
248
+ end
249
+ end
142
250
 
143
- it do
144
- Hallon::Session.should_receive(:instance).and_return(session)
145
- should be_local
251
+ describe "#local?" do
252
+ it "returns true for local tracks" do
253
+ local.should be_local
254
+ end
146
255
  end
147
256
  end
148
257
  end
@@ -1,7 +1,9 @@
1
1
  # coding: utf-8
2
2
 
3
3
  describe Hallon::User::Post do
4
- it { described_class.should include Hallon::Loadable }
4
+ let(:post) do
5
+ Hallon::User::Post.create("burgestrand", "These be some tight tracks, yo!", tracks)
6
+ end
5
7
 
6
8
  let(:tracks) do
7
9
  [].tap do |tracks|
@@ -10,9 +12,7 @@ describe Hallon::User::Post do
10
12
  end
11
13
  end
12
14
 
13
- let(:post) do
14
- stub_session { Hallon::User::Post.create("burgestrand", "These be some tight tracks, yo!", tracks) }
15
- end
15
+ specify { post.should be_a Hallon::Loadable }
16
16
 
17
17
  describe ".new" do
18
18
  it "should be private" do
@@ -27,7 +27,7 @@ describe Hallon::User::Post do
27
27
  end
28
28
 
29
29
  it "should allow you to post a single track" do
30
- post = stub_session { Hallon::User::Post.create("burgestrand", nil, tracks[0]) }
30
+ post = Hallon::User::Post.create("burgestrand", nil, tracks[0])
31
31
  post.tracks.should eq tracks[0, 1]
32
32
  end
33
33
  end
@@ -59,6 +59,11 @@ describe Hallon::User::Post do
59
59
  it "should return the message sent with the post" do
60
60
  post.message.should eq "These be some tight tracks, yo!"
61
61
  end
62
+
63
+ it "returns an empty string if no message was sent" do
64
+ post = Hallon::User::Post.create("burgestrand", nil, tracks)
65
+ post.message.should be_nil
66
+ end
62
67
  end
63
68
 
64
69
  describe "#recipient" do
@@ -1,80 +1,90 @@
1
1
  # coding: utf-8
2
2
  describe Hallon::User do
3
- it { described_class.should include Hallon::Loadable }
3
+ let(:user) do
4
+ Hallon::User.new(mock_users[:default])
5
+ end
6
+
7
+ let(:empty_user) do
8
+ Hallon::User.new(mock_users[:empty])
9
+ end
10
+
11
+ specify { user.should be_a Hallon::Loadable }
4
12
 
5
13
  it_should_behave_like "a Linkable object" do
6
14
  let(:spotify_uri) { "spotify:user:burgestrand" }
7
15
  let(:custom_object) { "burgestrand" }
8
16
  end
9
17
 
10
- describe "an instance" do
11
- let(:user) { Hallon::User.new(mock_user) }
18
+ describe "#loaded?" do
19
+ it "should return true as the user is loaded" do
20
+ user.should be_loaded
21
+ end
22
+ end
23
+
24
+ describe "#name" do
25
+ it "should be the canonical name" do
26
+ user.name.should eq "burgestrand"
27
+ end
12
28
 
13
- describe "#loaded?" do
14
- it "should return true as the user is loaded" do
15
- user.should be_loaded
16
- end
29
+ it "returns an empty string if the name is not available" do
30
+ empty_user.name.should be_empty
17
31
  end
32
+ end
18
33
 
19
- describe "#name" do
20
- it "should be the canonical name" do
21
- user.name.should eq "burgestrand"
22
- end
34
+ describe "#display_name" do
35
+ it "should be the users’ display name" do
36
+ user.display_name.should eq "Burgestrand"
23
37
  end
24
38
 
25
- describe "#display_name" do
26
- it "should be the users’ display name" do
27
- user.display_name.should eq "Burgestrand"
28
- end
39
+ it "returns an empty string if the display name is not available" do
40
+ empty_user.name.should be_empty
29
41
  end
42
+ end
30
43
 
31
- describe "#post" do
32
- let(:post) { mock_session { user.post(tracks) } }
33
- let(:tracks) { instantiate(Hallon::Track, mock_track, mock_track_two) }
44
+ describe "#post" do
45
+ let(:post) { user.post(tracks) }
46
+ let(:tracks) { instantiate(Hallon::Track, mock_track, mock_track_two) }
34
47
 
35
- it "should post to the correct user" do
36
- post.recipient_name.should eq user.name
37
- end
48
+ it "should post to the correct user" do
49
+ post.recipient_name.should eq user.name
50
+ end
38
51
 
39
- it "should post with the given message" do
40
- post.message.should be_nil
41
- stub_session { user.post("Hey ho!", tracks) }.message.should eq "Hey ho!"
42
- end
52
+ it "should post with the given message" do
53
+ post.message.should be_nil
54
+ user.post("Hey ho!", tracks).message.should eq "Hey ho!"
55
+ end
43
56
 
44
- it "should return nil on failure" do
45
- stub_session { user.post([]).should be_nil }
46
- end
57
+ it "should return nil on failure" do
58
+ user.post([]).should be_nil
47
59
  end
60
+ end
48
61
 
49
- describe "#starred" do
50
- let(:starred) { Hallon::Playlist.new("spotify:user:%s:starred" % user.name) }
62
+ describe "#starred" do
63
+ let(:starred) { Hallon::Playlist.new("spotify:user:%s:starred" % user.name) }
51
64
 
52
- it "should return the users’ starred playlist" do
53
- session.login 'Kim', 'pass'
54
- session.should be_logged_in
55
- mock_session { user.starred.should eq starred }
56
- end
65
+ it "should return the users’ starred playlist" do
66
+ session.login 'Kim', 'pass'
67
+ session.should be_logged_in
68
+ user.starred.should eq starred
69
+ end
57
70
 
58
- it "should return nil if not logged in" do
59
- session.should_not be_logged_in
60
- mock_session { user.starred.should be_nil }
61
- end
71
+ it "should return nil if not available" do
72
+ empty_user.starred.should be_nil
62
73
  end
74
+ end
63
75
 
64
- describe "#published" do
65
- let(:published) { Hallon::PlaylistContainer.new(mock_container) }
76
+ describe "#published" do
77
+ let(:published) { Hallon::PlaylistContainer.new(mock_container) }
66
78
 
67
- it "should return the playlist container of the user" do
68
- Spotify.registry_add("spotify:container:%s" % user.name, mock_container)
79
+ it "should return the playlist container of the user" do
80
+ Spotify.registry_add("spotify:container:%s" % user.name, mock_container)
69
81
 
70
- session.login('burgestrand', 'pass')
71
- mock_session { user.published.should eq published }
72
- end
82
+ session.login('burgestrand', 'pass')
83
+ user.published.should eq published
84
+ end
73
85
 
74
- it "should return nil if not logged in" do
75
- Spotify.should_receive(:session_publishedcontainer_for_user_create).and_return(null_pointer)
76
- mock_session { user.published.should be_nil }
77
- end
86
+ it "should return nil if not logged in" do
87
+ empty_user.published.should be_nil
78
88
  end
79
89
  end
80
90
  end
@@ -15,11 +15,22 @@ require 'hallon'
15
15
  # Bail on failure
16
16
  Thread.abort_on_exception = true
17
17
 
18
+ # We don’t do long running tests.
19
+ # Actually, some tests might deadlock, so we guard against
20
+ # them doing that. It’s annoying.
21
+ class SlowTestError < StandardError
22
+ end
23
+
18
24
  RSpec.configure do |config|
19
25
  config.treat_symbols_as_metadata_keys_with_true_values = true
20
26
 
21
27
  config.before do
22
28
  Hallon::Session.stub(:instance?).and_return(true)
29
+ Hallon::Session.stub(:instance).and_return(session)
30
+ end
31
+
32
+ config.after do
33
+ Spotify.registry_clean
23
34
  end
24
35
 
25
36
  def fixture_image_path
@@ -35,21 +46,6 @@ RSpec.configure do |config|
35
46
  pointers.map { |x| klass.new(*x) }
36
47
  end
37
48
 
38
- def mock_session(times = 1)
39
- Hallon::Session.should_receive(:instance).at_least(times).times.and_return(session)
40
- yield
41
- end
42
-
43
- def stub_session(target = nil)
44
- if target
45
- target.any_instance.stub(:session).and_return(session)
46
- else
47
- Hallon::Session.stub(:instance).and_return(session)
48
- end
49
-
50
- target || (yield if block_given?)
51
- end
52
-
53
49
  def pointer_array_with(*args)
54
50
  ary = FFI::MemoryPointer.new(:pointer, args.size)
55
51
  ary.write_array_of_pointer args
@@ -76,3 +72,32 @@ end
76
72
  # Requires supporting files in ./support/ and ./fixtures/
77
73
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
78
74
  Dir["#{File.dirname(__FILE__)}/fixtures/**/*.rb"].each {|f| require f}
75
+
76
+ RSpec.configure do |config|
77
+ config.before do
78
+ Spotify.registry_add 'spotify:image:3ad93423add99766e02d563605c6e76ed2b0e400', mock_image
79
+ Spotify.registry_add 'spotify:user:burgestrand:playlist:megaplaylist', mock_playlist_two
80
+ Spotify.registry_add 'spotify:search:my+%C3%A5+utf8+%EF%A3%BF+query', mock_search
81
+ Spotify.registry_add 'spotify:search:', mock_empty_search
82
+
83
+ Spotify.registry_add 'spotify:albumbrowse:1xvnWMz2PNFf7mXOSRuLws', mock_albumbrowse
84
+ Spotify.registry_add 'spotify:album:1xvnWMz2PNFf7mXOSRuLws', mock_album
85
+
86
+ Spotify.registry_add 'spotify:albumbrowse:thisisanemptyalbumyoow', mock_empty_albumbrowse
87
+ Spotify.registry_add 'spotify:album:thisisanemptyalbumyoow', mock_empty_album
88
+
89
+ Spotify.registry_add 'spotify:artist:3bftcFwl4vqRNNORRsqm1G', mock_artist
90
+ Spotify.registry_add 'spotify:artistbrowse:3bftcFwl4vqRNNORRsqm1G', mock_artistbrowse
91
+
92
+ Spotify.registry_add 'spotify:artist:thisisanemptyartistyow', mock_empty_artist
93
+ Spotify.registry_add 'spotify:artistbrowse:thisisanemptyartistyow', mock_empty_artistbrowse
94
+
95
+ Spotify.registry_add 'spotify:container:burgestrand', mock_container
96
+
97
+ Spotify.registry_add 'spotify:track:7N2Vc8u56VGA4KUrGbikC2', mock_track
98
+
99
+ Spotify.registry_add 'spotify:user:burgestrand', mock_user
100
+ Spotify.registry_add 'spotify:user:burgestrand:playlist:07AX9IY9Hqmj1RqltcG0fi', mock_playlist
101
+ Spotify.registry_add 'spotify:user:burgestrand:starred', mock_playlist
102
+ end
103
+ end