hallon 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +40 -0
- data/README.markdown +1 -1
- data/dev/application_key_converter.rb +11 -0
- data/examples/example_support.rb +4 -0
- data/examples/playing_audio.rb +1 -1
- data/lib/hallon.rb +11 -0
- data/lib/hallon/album_browse.rb +3 -3
- data/lib/hallon/artist_browse.rb +20 -4
- data/lib/hallon/blob.rb +11 -0
- data/lib/hallon/enumerator.rb +5 -0
- data/lib/hallon/ext/spotify.rb +2 -0
- data/lib/hallon/image.rb +7 -1
- data/lib/hallon/link.rb +5 -2
- data/lib/hallon/observable.rb +48 -1
- data/lib/hallon/player.rb +15 -26
- data/lib/hallon/playlist.rb +16 -25
- data/lib/hallon/playlist_container.rb +38 -0
- data/lib/hallon/search.rb +75 -4
- data/lib/hallon/session.rb +31 -42
- data/lib/hallon/toplist.rb +3 -3
- data/lib/hallon/track.rb +28 -10
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +68 -18
- data/spec/hallon/album_spec.rb +62 -27
- data/spec/hallon/artist_browse_spec.rb +106 -31
- data/spec/hallon/artist_spec.rb +32 -18
- data/spec/hallon/blob_spec.rb +6 -0
- data/spec/hallon/enumerator_spec.rb +10 -0
- data/spec/hallon/error_spec.rb +4 -4
- data/spec/hallon/hallon_spec.rb +1 -1
- data/spec/hallon/image_spec.rb +58 -47
- data/spec/hallon/link_spec.rb +51 -43
- data/spec/hallon/observable/album_browse_spec.rb +1 -1
- data/spec/hallon/observable/artist_browse_spec.rb +1 -1
- data/spec/hallon/observable/image_spec.rb +1 -1
- data/spec/hallon/observable/playlist_container_spec.rb +4 -4
- data/spec/hallon/observable/playlist_spec.rb +14 -14
- data/spec/hallon/observable/post_spec.rb +1 -1
- data/spec/hallon/observable/search_spec.rb +1 -1
- data/spec/hallon/observable/session_spec.rb +17 -17
- data/spec/hallon/observable/toplist_spec.rb +1 -1
- data/spec/hallon/observable_spec.rb +40 -6
- data/spec/hallon/player_spec.rb +1 -1
- data/spec/hallon/playlist_container_spec.rb +96 -13
- data/spec/hallon/playlist_spec.rb +180 -45
- data/spec/hallon/search_spec.rb +211 -28
- data/spec/hallon/session_spec.rb +44 -38
- data/spec/hallon/toplist_spec.rb +31 -14
- data/spec/hallon/track_spec.rb +159 -50
- data/spec/hallon/user_post_spec.rb +10 -5
- data/spec/hallon/user_spec.rb +60 -50
- data/spec/spec_helper.rb +40 -15
- data/spec/support/album_mocks.rb +30 -0
- data/spec/support/artist_mocks.rb +36 -0
- data/spec/support/common_objects.rb +0 -201
- data/spec/support/image_mocks.rb +34 -0
- data/spec/support/playlist_container_mocks.rb +36 -0
- data/spec/support/playlist_mocks.rb +70 -0
- data/spec/support/search_mocks.rb +23 -0
- data/spec/support/session_mocks.rb +33 -0
- data/spec/support/toplist_mocks.rb +19 -0
- data/spec/support/track_mocks.rb +28 -0
- data/spec/support/user_mocks.rb +20 -0
- metadata +40 -18
- data/spec/support/context_stub_session.rb +0 -5
data/spec/hallon/link_spec.rb
CHANGED
@@ -1,69 +1,77 @@
|
|
1
1
|
describe Hallon::Link do
|
2
|
-
|
2
|
+
let(:link) do
|
3
|
+
Hallon::Link.new("spotify:user:burgestrand")
|
4
|
+
end
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
25
|
+
it "should accept any object that supplies a #to_link method" do
|
26
|
+
link = Hallon::Link.new("spotify:user:burgestrand")
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
to_linkable = double
|
29
|
+
to_linkable.should_receive(:to_link).and_return(link)
|
29
30
|
|
30
|
-
|
31
|
-
end
|
31
|
+
Hallon::Link.new(to_linkable).should eq link
|
32
32
|
end
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
57
|
+
link.to_str(7).should == "spotify"
|
52
58
|
end
|
53
59
|
|
54
60
|
it "should be in UTF-8 encoding" do
|
55
|
-
|
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
|
-
|
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
|
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
|
79
|
-
|
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(
|
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(
|
98
|
+
objB.should_receive(:to_str).and_return(link.to_str)
|
91
99
|
|
92
|
-
|
93
|
-
|
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(
|
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
|
-
|
112
|
+
link.should eq object
|
105
113
|
end
|
106
114
|
end
|
107
115
|
|
@@ -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
|
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
|
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
|
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) { [
|
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
|
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]
|
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
|
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) { [
|
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) { [
|
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
|
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) { [
|
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)
|
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
|
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!"
|
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"
|
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)
|
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
|
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) { [
|
89
|
+
let(:output) { [] }
|
90
90
|
end
|
91
91
|
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
|
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) { [
|
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) { [
|
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
|
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"
|
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) { [
|
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)
|
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) { [
|
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) { [
|
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) { [
|
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) { [
|
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) { [
|
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
|
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) { [
|
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!"
|
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) { [
|
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
|
145
|
+
let(:output) { [:ok] }
|
146
146
|
end
|
147
147
|
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 "#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
|