hallon 0.15.0 → 0.16.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 (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,69 +1,77 @@
1
1
  describe Hallon::Link do
2
- subject { Hallon::Link.new("spotify:user:burgestrand") }
2
+ let(:link) do
3
+ Hallon::Link.new("spotify:user:burgestrand")
4
+ end
3
5
 
4
- context "class methods" do
5
- describe "::new" do
6
- it "should raise an ArgumentError on an invalid link" do
7
- expect { Hallon::Link.new("omgwtfbbq") }.to raise_error(ArgumentError, /omgwtfbbq/)
8
- end
6
+ describe ".new" do
7
+ it "should raise an ArgumentError on an invalid link" do
8
+ expect { Hallon::Link.new("omgwtfbbq") }.to raise_error(ArgumentError, /omgwtfbbq/)
9
+ end
9
10
 
10
- it "should not raise error on valid links" do
11
- expect { Hallon::Link.new("spotify:user:burgestrand") }.to_not raise_error
12
- end
11
+ it "should not raise error on valid links" do
12
+ expect { Hallon::Link.new("spotify:user:burgestrand") }.to_not raise_error
13
+ end
13
14
 
14
- it "should raise an error given a null pointer" do
15
- expect { Hallon::Link.new(Spotify::Pointer.new(null_pointer, :link, false)) }.to raise_error(ArgumentError)
16
- end
15
+ it "should raise an error given a null pointer" do
16
+ expect { Hallon::Link.new(Spotify::Pointer.new(null_pointer, :link, false)) }.to raise_error(ArgumentError)
17
+ end
17
18
 
18
- it "should raise an error when no session instance is about" do
19
- # this is due to a bug in libspotify, it will segfault otherwise
20
- Hallon::Session.stub(:instance?).and_return(false)
21
- expect { Hallon::Link.new("spotify:user:burgestrand") }.to raise_error(/session/i)
22
- end
19
+ it "should raise an error when no session instance is about" do
20
+ # this is due to a bug in libspotify, it will segfault otherwise
21
+ Hallon::Session.stub(:instance?).and_return(false)
22
+ expect { Hallon::Link.new("spotify:user:burgestrand") }.to raise_error(Hallon::NoSessionError)
23
+ end
23
24
 
24
- it "should accept any object that supplies a #to_link method" do
25
- link = Hallon::Link.new("spotify:user:burgestrand")
25
+ it "should accept any object that supplies a #to_link method" do
26
+ link = Hallon::Link.new("spotify:user:burgestrand")
26
27
 
27
- to_linkable = double
28
- to_linkable.should_receive(:to_link).and_return(link)
28
+ to_linkable = double
29
+ to_linkable.should_receive(:to_link).and_return(link)
29
30
 
30
- Hallon::Link.new(to_linkable).should eq link
31
- end
31
+ Hallon::Link.new(to_linkable).should eq link
32
32
  end
33
+ end
33
34
 
34
- describe "::valid?" do
35
- it "should be true for a valid link" do
36
- Hallon::Link.valid?("spotify:user:burgestrand").should be_true
37
- end
35
+ describe ".valid?" do
36
+ it "should be true for a valid link" do
37
+ Hallon::Link.valid?("spotify:user:burgestrand").should be_true
38
+ end
38
39
 
39
- it "should be false for an invalid link" do
40
- Hallon::Link.valid?("omgwtfbbq").should be_false
41
- end
40
+ it "should be false for an invalid link" do
41
+ Hallon::Link.valid?("omgwtfbbq").should be_false
42
+ end
43
+
44
+ it "raises an error when no session has been initialized" do
45
+ # this is due to a bug in libspotify, it will segfault otherwise
46
+ Hallon::Session.stub(:instance?).and_return(false)
47
+ expect { Hallon::Link.valid?("omgwtfbbq") }.to raise_error(Hallon::NoSessionError)
42
48
  end
43
49
  end
44
50
 
45
51
  describe "#to_str" do
46
52
  it "should return the Spotify URI as a string" do
47
- subject.to_str.should == "spotify:user:burgestrand"
53
+ link.to_str.should == "spotify:user:burgestrand"
48
54
  end
49
55
 
50
56
  it "should truncate if given a small maximum length" do
51
- subject.to_str(7).should == "spotify"
57
+ link.to_str(7).should == "spotify"
52
58
  end
53
59
 
54
60
  it "should be in UTF-8 encoding" do
55
- subject.to_str.encoding.should eq Encoding::UTF_8
61
+ link.to_str.encoding.should eq Encoding::UTF_8
56
62
  end
57
63
  end
58
64
 
59
65
  describe "#to_url" do
60
66
  it "should return the correct http URL" do
61
- subject.to_url.should == "http://open.spotify.com/user/burgestrand"
67
+ link.to_url.should == "http://open.spotify.com/user/burgestrand"
62
68
  end
63
69
  end
64
70
 
65
71
  describe "#length" do
66
- it { subject.length.should == "spotify:user:burgestrand".length }
72
+ it "returns the image length" do
73
+ link.length.should == "spotify:user:burgestrand".length
74
+ end
67
75
  end
68
76
 
69
77
  describe "#type" do
@@ -75,8 +83,8 @@ describe Hallon::Link do
75
83
  end
76
84
 
77
85
  describe "#to_s" do
78
- it("should include the Spotify URI") do
79
- subject.to_s.should include subject.to_str
86
+ it "includes the image URI" do
87
+ link.to_s.should include link.to_uri
80
88
  end
81
89
  end
82
90
 
@@ -85,23 +93,23 @@ describe Hallon::Link do
85
93
  objA = double
86
94
  objA.should_not_receive(:to_str)
87
95
 
88
- objB = Hallon::Link.new(subject.to_str)
96
+ objB = Hallon::Link.new(link.to_str)
89
97
  objB.should_receive(:pointer).and_return(null_pointer)
90
- objB.should_receive(:to_str).and_return(subject.to_str)
98
+ objB.should_receive(:to_str).and_return(link.to_str)
91
99
 
92
- subject.should_not eq objA
93
- subject.should eq objB
100
+ link.should_not eq objA
101
+ link.should eq objB
94
102
  end
95
103
 
96
104
  it "should compare underlying pointers if #to_str is unavailable" do
97
- object = Hallon::Link.new(subject.pointer)
105
+ object = Hallon::Link.new(link.pointer)
98
106
 
99
107
  def object.respond_to?(o)
100
108
  return false if o == :to_str
101
109
  super
102
110
  end
103
111
 
104
- subject.should eq object
112
+ link.should eq object
105
113
  end
106
114
  end
107
115
 
@@ -2,6 +2,6 @@ describe Hallon::Observable::AlbumBrowse do
2
2
  specification_for_callback "load" do
3
3
  let(:type) { :albumbrowse_complete_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
@@ -2,6 +2,6 @@ describe Hallon::Observable::ArtistBrowse do
2
2
  specification_for_callback "load" do
3
3
  let(:type) { :artistbrowse_complete_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
@@ -2,7 +2,7 @@ describe Hallon::Observable::Image do
2
2
  specification_for_callback "load" do
3
3
  let(:type) { :image_loaded_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
8
8
 
@@ -1,21 +1,21 @@
1
1
  describe Hallon::Observable::PlaylistContainer do
2
2
  specification_for_callback "playlist_added" do
3
3
  let(:input) { [a_pointer, mock_playlist_raw, 3, :userdata] }
4
- let(:output) { [Hallon::Playlist.new(mock_playlist), 3, subject] }
4
+ let(:output) { [Hallon::Playlist.new(mock_playlist), 3] }
5
5
  end
6
6
 
7
7
  specification_for_callback "playlist_removed" do
8
8
  let(:input) { [a_pointer, mock_playlist_raw, 3, :userdata] }
9
- let(:output) { [Hallon::Playlist.new(mock_playlist), 3, subject] }
9
+ let(:output) { [Hallon::Playlist.new(mock_playlist), 3] }
10
10
  end
11
11
 
12
12
  specification_for_callback "playlist_moved" do
13
13
  let(:input) { [a_pointer, mock_playlist_raw, 3, 8, :userdata] }
14
- let(:output) { [Hallon::Playlist.new(mock_playlist), 3, 8, subject] }
14
+ let(:output) { [Hallon::Playlist.new(mock_playlist), 3, 8] }
15
15
  end
16
16
 
17
17
  specification_for_callback "container_loaded" do
18
18
  let(:input) { [a_pointer, :userdata] }
19
- let(:output) { [subject] }
19
+ let(:output) { [] }
20
20
  end
21
21
  end
@@ -18,67 +18,67 @@ describe Hallon::Observable::Playlist do
18
18
 
19
19
  specification_for_callback "tracks_added" do
20
20
  let(:input) { [a_pointer, trackpointers, trackpointers_size, 0, :userdata] }
21
- let(:output) { [tracks, 0, subject] }
21
+ let(:output) { [tracks, 0] }
22
22
  end
23
23
 
24
24
  specification_for_callback "tracks_removed" do
25
25
  let(:input) { [a_pointer, track_index_pointers, trackpointers_size, :userdata] }
26
- let(:output) { [[0, 1], subject] }
26
+ let(:output) { [[0, 1]] }
27
27
  end
28
28
 
29
29
  specification_for_callback "tracks_moved" do
30
30
  let(:input) { [a_pointer, track_index_pointers, trackpointers_size, 7, :userdata] }
31
- let(:output) { [[0, 1], 7, subject] }
31
+ let(:output) { [[0, 1], 7] }
32
32
  end
33
33
 
34
34
  specification_for_callback "playlist_renamed" do
35
35
  let(:input) { [a_pointer, :userdata] }
36
- let(:output) { [subject] }
36
+ let(:output) { [] }
37
37
  end
38
38
 
39
39
  specification_for_callback "playlist_state_changed" do
40
40
  let(:input) { [a_pointer, :userdata] }
41
- let(:output) { [subject] }
41
+ let(:output) { [] }
42
42
  end
43
43
 
44
44
  specification_for_callback "playlist_update_in_progress" do
45
45
  let(:input) { [a_pointer, true, :userdata] }
46
- let(:output) { [true, subject] }
46
+ let(:output) { [true] }
47
47
  end
48
48
 
49
49
  specification_for_callback "playlist_metadata_updated" do
50
50
  let(:input) { [a_pointer, :userdata] }
51
- let(:output) { [subject] }
51
+ let(:output) { [] }
52
52
  end
53
53
 
54
54
  specification_for_callback "track_created_changed" do
55
55
  let(:input) { [a_pointer, 7, mock_user_raw, 15, :userdata] }
56
- let(:output) { [7, Hallon::User.new(mock_user), Time.at(15), subject] }
56
+ let(:output) { [7, Hallon::User.new(mock_user), Time.at(15)] }
57
57
  end
58
58
 
59
59
  specification_for_callback "track_seen_changed" do
60
60
  let(:input) { [a_pointer, 0, true, :userdata] }
61
- let(:output) { [0, true, subject] }
61
+ let(:output) { [0, true] }
62
62
  end
63
63
 
64
64
  specification_for_callback "track_message_changed" do
65
65
  let(:input) { [a_pointer, 13, "I LUFF JOO!", :userdata] }
66
- let(:output) { [13, "I LUFF JOO!", subject] }
66
+ let(:output) { [13, "I LUFF JOO!"] }
67
67
  end
68
68
 
69
69
  specification_for_callback "description_changed" do
70
70
  let(:input) { [a_pointer, "Merily merily merily bong", :userdata] }
71
- let(:output) { ["Merily merily merily bong", subject] }
71
+ let(:output) { ["Merily merily merily bong"] }
72
72
  end
73
73
 
74
74
  specification_for_callback "image_changed" do
75
75
  before { Hallon::Session.stub!(:instance => session) }
76
76
  let(:input) { [a_pointer, mock_image_id, :userdata] }
77
- let(:output) { [Hallon::Image.new(mock_image), subject] }
77
+ let(:output) { [Hallon::Image.new(mock_image)] }
78
78
 
79
79
  it "should not fail if the image has been *removed*" do
80
80
  block = proc { |image| }
81
- block.should_receive(:call).with(nil, subject)
81
+ block.should_receive(:call).with(nil)
82
82
  subject.on(:image_changed, &block)
83
83
  subject_callback.call(a_pointer, null_pointer, :userdata)
84
84
  end
@@ -86,6 +86,6 @@ describe Hallon::Observable::Playlist do
86
86
 
87
87
  specification_for_callback "subscribers_changed" do
88
88
  let(:input) { [a_pointer, :userdata] }
89
- let(:output) { [subject] }
89
+ let(:output) { [] }
90
90
  end
91
91
  end
@@ -2,7 +2,7 @@ describe Hallon::Observable::Post do
2
2
  specification_for_callback "complete" do
3
3
  let(:type) { :inboxpost_complete_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
8
8
 
@@ -2,6 +2,6 @@ describe Hallon::Observable::Search do
2
2
  specification_for_callback "load" do
3
3
  let(:type) { :search_complete_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
@@ -5,32 +5,32 @@ describe Hallon::Observable::Session do
5
5
 
6
6
  specification_for_callback "logged_in" do
7
7
  let(:input) { [a_pointer, :ok] }
8
- let(:output) { [:ok, subject] }
8
+ let(:output) { [:ok] }
9
9
  end
10
10
 
11
11
  specification_for_callback "logged_out" do
12
12
  let(:input) { [a_pointer] }
13
- let(:output) { [subject] }
13
+ let(:output) { [] }
14
14
  end
15
15
 
16
16
  specification_for_callback "metadata_updated" do
17
17
  let(:input) { [a_pointer] }
18
- let(:output) { [subject] }
18
+ let(:output) { [] }
19
19
  end
20
20
 
21
21
  specification_for_callback "connection_error" do
22
22
  let(:input) { [a_pointer, :ok] }
23
- let(:output) { [:ok, subject] }
23
+ let(:output) { [:ok] }
24
24
  end
25
25
 
26
26
  specification_for_callback "message_to_user" do
27
27
  let(:input) { [a_pointer, "ALL UR BASE"] }
28
- let(:output) { ["ALL UR BASE", subject] }
28
+ let(:output) { ["ALL UR BASE"] }
29
29
  end
30
30
 
31
31
  specification_for_callback "notify_main_thread" do
32
32
  let(:input) { [a_pointer] }
33
- let(:output) { [subject] }
33
+ let(:output) { [] }
34
34
  end
35
35
 
36
36
  specification_for_callback "music_delivery" do
@@ -58,7 +58,7 @@ describe Hallon::Observable::Session do
58
58
  end
59
59
 
60
60
  let(:input) { [a_pointer, format, frames, num_frames] }
61
- let(:output) { [{rate: 44100, type: :int16, channels: 2}, data.each_slice(2), subject] }
61
+ let(:output) { [{rate: 44100, type: :int16, channels: 2}, data.each_slice(2)] }
62
62
 
63
63
  it "should return the resulting value" do
64
64
  subject.on(:music_delivery) { 7 }
@@ -76,27 +76,27 @@ describe Hallon::Observable::Session do
76
76
 
77
77
  specification_for_callback "play_token_lost" do
78
78
  let(:input) { [a_pointer] }
79
- let(:output) { [subject] }
79
+ let(:output) { [] }
80
80
  end
81
81
 
82
82
  specification_for_callback "end_of_track" do
83
83
  let(:input) { [a_pointer] }
84
- let(:output) { [subject] }
84
+ let(:output) { [] }
85
85
  end
86
86
 
87
87
  specification_for_callback "start_playback" do
88
88
  let(:input) { [a_pointer] }
89
- let(:output) { [subject] }
89
+ let(:output) { [] }
90
90
  end
91
91
 
92
92
  specification_for_callback "stop_playback" do
93
93
  let(:input) { [a_pointer] }
94
- let(:output) { [subject] }
94
+ let(:output) { [] }
95
95
  end
96
96
 
97
97
  specification_for_callback "get_audio_buffer_stats" do
98
98
  let(:input) { [a_pointer, Spotify::AudioBufferStats.new.pointer] }
99
- let(:output) { [subject] }
99
+ let(:output) { [] }
100
100
 
101
101
  it "should return the resulting audio buffer stats" do
102
102
  stats = Spotify::AudioBufferStats.new
@@ -122,26 +122,26 @@ describe Hallon::Observable::Session do
122
122
 
123
123
  specification_for_callback "streaming_error" do
124
124
  let(:input) { [a_pointer, :ok] }
125
- let(:output) { [:ok, subject] }
125
+ let(:output) { [:ok] }
126
126
  end
127
127
 
128
128
  specification_for_callback "userinfo_updated" do
129
129
  let(:input) { [a_pointer] }
130
- let(:output) { [subject] }
130
+ let(:output) { [] }
131
131
  end
132
132
 
133
133
  specification_for_callback "log_message" do
134
134
  let(:input) { [a_pointer, "WATCHING U!"] }
135
- let(:output) { ["WATCHING U!", subject] }
135
+ let(:output) { ["WATCHING U!"] }
136
136
  end
137
137
 
138
138
  specification_for_callback "offline_status_updated" do
139
139
  let(:input) { [a_pointer] }
140
- let(:output) { [subject] }
140
+ let(:output) { [] }
141
141
  end
142
142
 
143
143
  specification_for_callback "offline_error" do
144
144
  let(:input) { [a_pointer, :ok] }
145
- let(:output) { [:ok, subject] }
145
+ let(:output) { [:ok] }
146
146
  end
147
147
  end
@@ -2,6 +2,6 @@ describe Hallon::Observable::Toplist do
2
2
  specification_for_callback "load" do
3
3
  let(:type) { :toplistbrowse_complete_cb }
4
4
  let(:input) { [a_pointer, :userdata] }
5
- let(:output) { [subject] }
5
+ let(:output) { [] }
6
6
  end
7
7
  end
@@ -45,6 +45,10 @@ describe Hallon::Observable do
45
45
  def pointer
46
46
  FFI::Pointer.new(0xDEADBEEF)
47
47
  end
48
+
49
+ def session
50
+ Hallon::Session.instance
51
+ end
48
52
  end
49
53
  end
50
54
 
@@ -159,12 +163,42 @@ describe Hallon::Observable do
159
163
  end
160
164
  end
161
165
 
162
- describe "#trigger" do
163
- it "should always append self to the arguments" do
164
- block = proc {}
165
- subject.on(:testing, &block)
166
- block.should_receive(:call).with(subject)
167
- subject.send(:trigger, :testing)
166
+ describe "#wait_for" do
167
+ around(:each) do |example|
168
+ Timeout.timeout(0.5, SlowTestError, &example)
169
+ end
170
+
171
+ it "should not call the given block on notify main thread event" do
172
+ notified = false
173
+ counter = 0
174
+
175
+ session.should_receive(:process_events).twice.and_return do
176
+ if notified
177
+ subject.class.send(:testing_callback, subject.pointer)
178
+ else
179
+ session.class.send(:notify_main_thread_callback, session.pointer)
180
+ notified = true
181
+ end
182
+
183
+ 0
184
+ end
185
+
186
+ subject.wait_for(:testing) do |event|
187
+ event.should eq :testing if (counter += 1) > 1
188
+ end
189
+ end
190
+
191
+ it "should time out if waiting for events too long" do
192
+ counter = 0
193
+ session.should_receive(:process_events).once.and_return(0) # and do nothing
194
+ subject.wait_for(:testing) do |event|
195
+ event.should be_nil if (counter += 1) > 1
196
+ end
197
+ end
198
+
199
+ it "should call the given block once before waiting" do
200
+ session.should_not_receive(:process_events)
201
+ subject.wait_for { true }
168
202
  end
169
203
  end
170
204