cultome_player 2.0.0 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -79
- data/Rakefile +38 -4
- data/bin/cultome_player +16 -4
- data/config/environment.yml +4 -0
- data/cultome_player.gemspec +8 -1
- data/lib/cultome_player.rb +45 -19
- data/lib/cultome_player/command/language.rb +3 -4
- data/lib/cultome_player/command/processor.rb +9 -7
- data/lib/cultome_player/command/reader.rb +11 -2
- data/lib/cultome_player/environment.rb +21 -13
- data/lib/cultome_player/events.rb +7 -7
- data/lib/cultome_player/objects/album.rb +9 -2
- data/lib/cultome_player/objects/artist.rb +9 -2
- data/lib/cultome_player/objects/command.rb +10 -1
- data/lib/cultome_player/objects/drive.rb +4 -1
- data/lib/cultome_player/objects/genre.rb +4 -1
- data/lib/cultome_player/objects/parameter.rb +21 -1
- data/lib/cultome_player/objects/response.rb +5 -1
- data/lib/cultome_player/objects/song.rb +9 -4
- data/lib/cultome_player/player/adapter/mpg123.rb +3 -4
- data/lib/cultome_player/player/interactive.rb +67 -11
- data/lib/cultome_player/player/interface/basic.rb +13 -9
- data/lib/cultome_player/player/interface/extended.rb +26 -12
- data/lib/cultome_player/player/interface/helper.rb +73 -9
- data/lib/cultome_player/plugins.rb +44 -18
- data/lib/cultome_player/plugins/alias.rb +85 -0
- data/lib/cultome_player/plugins/gestures.rb +90 -0
- data/lib/cultome_player/plugins/help.rb +4 -4
- data/lib/cultome_player/plugins/keyboard_special_keys.rb +22 -0
- data/lib/cultome_player/plugins/points.rb +51 -0
- data/lib/cultome_player/utils.rb +117 -7
- data/lib/cultome_player/version.rb +1 -1
- data/xbindkeys/xbindkeysrc +12 -0
- metadata +37 -89
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -24
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/db/001_create_schema.rb +0 -58
- data/spec/config.yml +0 -0
- data/spec/cultome_player/command/processor_spec.rb +0 -168
- data/spec/cultome_player/command/reader_spec.rb +0 -45
- data/spec/cultome_player/cultome_player_spec.rb +0 -17
- data/spec/cultome_player/environment_spec.rb +0 -65
- data/spec/cultome_player/events_spec.rb +0 -22
- data/spec/cultome_player/media_spec.rb +0 -41
- data/spec/cultome_player/player/adapter/mpg123_spec.rb +0 -82
- data/spec/cultome_player/player/interface/basic_spec.rb +0 -168
- data/spec/cultome_player/player/interface/extended/connect_spec.rb +0 -117
- data/spec/cultome_player/player/interface/extended/search_spec.rb +0 -90
- data/spec/cultome_player/player/interface/extended/show_spec.rb +0 -36
- data/spec/cultome_player/player/interface/extended/shuffle_spec.rb +0 -26
- data/spec/cultome_player/player/interface/extended_spec.rb +0 -136
- data/spec/cultome_player/player/interface/helper_spec.rb +0 -63
- data/spec/cultome_player/player/interface_spec.rb +0 -17
- data/spec/cultome_player/player/playlist_spec.rb +0 -301
- data/spec/cultome_player/plugins/help_spec.rb +0 -21
- data/spec/cultome_player/plugins_spec.rb +0 -19
- data/spec/cultome_player/utils_spec.rb +0 -15
- data/spec/spec_helper.rb +0 -108
- data/spec/test/uno/dos/dos.mp3 +0 -0
- data/spec/test/uno/dos/tres/tres.mp3 +0 -0
- data/spec/test/uno/uno.mp3 +0 -0
- data/tasks/console.rake +0 -19
- data/tasks/db.rake +0 -19
- data/tasks/run.rake +0 -7
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe CultomePlayer::Player::Adapter::Mpg123 do
|
4
|
-
let(:t){ TestClass.new }
|
5
|
-
let(:song) do
|
6
|
-
s = Song.new
|
7
|
-
s.stub(:path){'/duck/path'}
|
8
|
-
s
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'can check if player is running' do
|
12
|
-
t.should respond_to :player_running?
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'start the player if is not running' do
|
16
|
-
t.should_receive :start_player
|
17
|
-
t.should_not be_player_running
|
18
|
-
t.play_in_player song
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'with active playback' do
|
22
|
-
before :each do
|
23
|
-
t.play_in_player song
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'pause a song' do
|
27
|
-
t.should_receive(:send_to_player).with('pause')
|
28
|
-
t.pause_in_player
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'stops a song' do
|
32
|
-
t.should_receive(:send_to_player).with('stop')
|
33
|
-
t.stop_in_player
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'resume a paused song' do
|
37
|
-
t.pause_in_player
|
38
|
-
t.should_receive(:send_to_player).with("pause")
|
39
|
-
t.resume_in_player
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'resume a stopped song' do
|
43
|
-
t.stop_in_player
|
44
|
-
t.should_receive(:start_player)
|
45
|
-
t.play_in_player song
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'running actual mpg123 player', mplayer: true do
|
50
|
-
it 'dont show the initial text from mpg123' do
|
51
|
-
th = Thread.new do
|
52
|
-
class MplayerTest
|
53
|
-
include CultomePlayer
|
54
|
-
end
|
55
|
-
|
56
|
-
song1 = double(:song1, path: "/home/csoria/music/unknown/Torches/02 Pumped Up Kicks.mp3")
|
57
|
-
song2 = double(:song2, path: "/home/csoria/music/unknown/Torches/07 Houdini.mp3")
|
58
|
-
p = MplayerTest.new
|
59
|
-
p.prepare_environment(:rspec)
|
60
|
-
|
61
|
-
puts "Primera cancion..."
|
62
|
-
p.play_in_player(song1)
|
63
|
-
i = 0
|
64
|
-
while i < 3
|
65
|
-
sleep 1
|
66
|
-
i += 1
|
67
|
-
end
|
68
|
-
|
69
|
-
puts "Segunda cancion(running: #{p.player_running?})..."
|
70
|
-
p.play_in_player(song2)
|
71
|
-
i = 0
|
72
|
-
while i < 3
|
73
|
-
sleep 1
|
74
|
-
i += 1
|
75
|
-
end
|
76
|
-
puts "Terminado!"
|
77
|
-
end # Thread
|
78
|
-
|
79
|
-
th.join
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,168 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe CultomePlayer::Player::Interface::Basic do
|
4
|
-
let(:t){ TestClass.new }
|
5
|
-
|
6
|
-
describe '#pause' do
|
7
|
-
it 'calls the underliying player' do
|
8
|
-
t.should_receive(:pause_in_player)
|
9
|
-
t.execute "pause on"
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'respond to description_help' do
|
13
|
-
t.should respond_to(:description_help)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'respond to usage_help' do
|
17
|
-
t.should respond_to(:usage_help)
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'respond with Response object' do
|
21
|
-
t.execute("pause on").should be_instance_of Response
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe '#stop' do
|
26
|
-
before :each do
|
27
|
-
t.execute "connect '#{test_folder}' => test"
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'calls the underliying player' do
|
31
|
-
t.should_receive(:stop_in_player)
|
32
|
-
t.execute "play"
|
33
|
-
t.execute "stop"
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'respond to description_stop' do
|
37
|
-
t.should respond_to(:description_stop)
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'respond to usage_stop' do
|
41
|
-
t.should respond_to(:usage_stop)
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'respond with Response object' do
|
45
|
-
t.execute("stop").should be_instance_of Response
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe '#play' do
|
50
|
-
|
51
|
-
it 'respond to description_play' do
|
52
|
-
t.should respond_to(:description_play)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'respond to usage_play' do
|
56
|
-
t.should respond_to(:usage_play)
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'respond with Response object' do
|
60
|
-
t.execute("play").should be_instance_of Response
|
61
|
-
end
|
62
|
-
|
63
|
-
context 'without music connected' do
|
64
|
-
it 'advise to connect music first if no music is connected' do
|
65
|
-
r = t.execute("play")
|
66
|
-
r.message.should eq "No music connected! You should try 'connect /home/yoo/music => main' first"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'with music connected' do
|
71
|
-
before :each do
|
72
|
-
t.execute "connect '#{test_folder}' => test"
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'calls the underliying player to play' do
|
76
|
-
t.should_receive(:play_in_player)
|
77
|
-
t.execute 'play'
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'calls the underliying player to resume playback' do
|
81
|
-
t.should_receive(:resume_in_player)
|
82
|
-
t.execute 'play'
|
83
|
-
t.execute "pause on"
|
84
|
-
t.execute 'play'
|
85
|
-
end
|
86
|
-
|
87
|
-
context 'without parameters' do
|
88
|
-
it 'in clean state should play all the library' do
|
89
|
-
r = t.execute 'play'
|
90
|
-
r.playlist.should have(3).items
|
91
|
-
r.should respond_to(:now_playing)
|
92
|
-
r.should respond_to(:playlist)
|
93
|
-
t.current_playlist.to_a.should have(3).items
|
94
|
-
t.current_song.should_not be_nil
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'if paused should resume playback' do
|
98
|
-
t.execute "pause on"
|
99
|
-
t.should be_paused
|
100
|
-
t.execute 'play'
|
101
|
-
t.should_not be_paused
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'if stopped should resume last playback from the begin' do
|
105
|
-
t.execute "play"
|
106
|
-
my_song = t.current_song
|
107
|
-
t.execute "stop"
|
108
|
-
t.should be_stopped
|
109
|
-
t.execute 'play'
|
110
|
-
t.should be_playing
|
111
|
-
my_song.should eq t.current_song
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'get an error message if playing' do
|
115
|
-
t.execute 'play'
|
116
|
-
r = t.execute 'play'
|
117
|
-
r.message.should eq 'What you mean? Im already playing!'
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
context 'with number parameter' do
|
122
|
-
it 'play the nth element in focus list without altering the current playlist' do
|
123
|
-
t.execute 'search a'
|
124
|
-
t.execute 'play 2'
|
125
|
-
t.current_song.name.should eq t.playlists[:focus].at(1).name
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
context 'with criteria parameter' do
|
130
|
-
it 'different criterias create an AND filter' do
|
131
|
-
t.execute "play t:a"
|
132
|
-
t.current_playlist.should have(2).songs
|
133
|
-
t.execute "play t:a a:m"
|
134
|
-
t.current_playlist.should have(1).songs
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'same criterias create an OR filter' do
|
138
|
-
t.execute "play t:a"
|
139
|
-
t.current_playlist.should have(2).songs
|
140
|
-
t.execute "play t:a t:d"
|
141
|
-
t.current_playlist.should have(3).songs
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context 'with object parameter' do
|
146
|
-
it 'replace the current playlist with the object content' do
|
147
|
-
t.execute 'play'
|
148
|
-
t.current_playlist.should have(3).songs
|
149
|
-
t.execute 'next'
|
150
|
-
t.execute 'next'
|
151
|
-
t.execute 'play @history'
|
152
|
-
t.current_playlist.should have(2).songs
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
context 'with literal parameter' do
|
157
|
-
it 'create an OR filter with the fields trackname, artist and album' do
|
158
|
-
t.execute 'play way'
|
159
|
-
t.current_playlist.should have(1).songs
|
160
|
-
t.execute 'play sing'
|
161
|
-
t.current_playlist.should have(1).songs
|
162
|
-
t.execute 'play sing way'
|
163
|
-
t.current_playlist.should have(2).songs
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
@@ -1,117 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
include CultomePlayer::Objects
|
4
|
-
|
5
|
-
describe CultomePlayer::Player::Interface::Extended do
|
6
|
-
let(:t){ TestClass.new }
|
7
|
-
|
8
|
-
before :each do
|
9
|
-
Song.create!(id: 1, name: "song_uno", artist_id: 1, album_id: 1, drive_id: 1, relative_path: "uno/uno.mp3")
|
10
|
-
Song.create!(id: 2, name: "song_dos", artist_id: 1, album_id: 1, drive_id: 1, relative_path: "uno/dos/dos.mp3")
|
11
|
-
|
12
|
-
Artist.create!(id: 1, name: "artist_uno")
|
13
|
-
|
14
|
-
Album.create!(id: 1, name: "album_uno")
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'detect all the files in the subdirectories' do
|
18
|
-
r = t.execute "connect '#{test_folder}' => test"
|
19
|
-
r.files_detected.should eq 3
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'import the files not imported before and update the rest' do
|
23
|
-
r = t.execute "connect '#{test_folder}' => test"
|
24
|
-
r.files_detected.should eq 3
|
25
|
-
r.files_updated.should eq 2
|
26
|
-
r.files_imported.should eq 1
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'with only a literal parameter' do
|
30
|
-
it 'connect the named drive if exist' do
|
31
|
-
Drive.create!(name: "test", path: test_folder, connected: false)
|
32
|
-
Drive.find_by(name: 'test').should_not be_connected
|
33
|
-
r = t.execute "connect test"
|
34
|
-
Drive.find_by(name: 'test').should be_connected
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'raise an error if drive not exists' do
|
38
|
-
r = t.execute("connect ghost")
|
39
|
-
r.message.should eq 'invalid name'
|
40
|
-
r.details.should eq 'the named drive doesnt exists'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context 'with a path and a literal' do
|
45
|
-
it 'create the drive if not exists' do
|
46
|
-
Drive.find_by(name: 'new').should be_nil
|
47
|
-
r = t.execute "connect '#{test_folder}' => new"
|
48
|
-
Drive.find_by(name: 'new').should_not be_nil
|
49
|
-
r.drive_updated.should be_false
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'update the drive if exists' do
|
53
|
-
Drive.create!(name: "test", path: test_folder)
|
54
|
-
r = t.execute "connect '#{test_folder}' => test"
|
55
|
-
r.drive_updated.should be_true
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'raise an error if path is invalid' do
|
59
|
-
r = t.execute('connect /invalid => ghost')
|
60
|
-
r.message.should eq 'invalid path'
|
61
|
-
r.details.should eq 'the directory is invalid'
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
describe 'with mp3 files' do
|
66
|
-
let(:t2){ TestClass.new }
|
67
|
-
|
68
|
-
before :each do
|
69
|
-
Drive.create!(name: "library", path: test_folder)
|
70
|
-
end
|
71
|
-
|
72
|
-
describe 'create information from file' do
|
73
|
-
it 'create the song' do
|
74
|
-
expect{ t2.execute "connect '#{test_folder}' => test" }.to change{ Song.all.count }.by(1)
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'create the artist' do
|
78
|
-
expect{ t2.execute "connect '#{test_folder}' => test" }.to change{ Artist.all.count }.by(2) # una de las rolas no tiene artist
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'create the album' do
|
82
|
-
expect{ t2.execute "connect '#{test_folder}' => test" }.to change{ Album.all.count }.by(2) # una de las rolas no tiene album
|
83
|
-
end
|
84
|
-
|
85
|
-
context 'already imported into library' do
|
86
|
-
before :each do
|
87
|
-
DatabaseCleaner.clean_with(:truncation)
|
88
|
-
r = t2.execute "connect '#{test_folder}/uno/dos/tres' => test"
|
89
|
-
r.files_imported.should eq 1
|
90
|
-
r.files_updated.should eq 0
|
91
|
-
@track = Song.all.first
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'insert the correct song information' do
|
95
|
-
@track.name.should eq "Sing For Absolution"
|
96
|
-
@track.year.should eq 2003
|
97
|
-
@track.track.should eq 4
|
98
|
-
@track.duration.should eq 295
|
99
|
-
@track.relative_path.should eq "tres.mp3"
|
100
|
-
# dependencias
|
101
|
-
@track.artist_id.should eq 1
|
102
|
-
@track.album_id.should eq 1
|
103
|
-
@track.drive_id.should eq 1
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'insert the correct artist information' do
|
107
|
-
@track.artist.name.should eq "Muse"
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'insert the correct album information' do
|
111
|
-
@track.album.name.should eq "Absolution"
|
112
|
-
@track.album.artists.first.name.should eq "Muse"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
include CultomePlayer::Objects
|
4
|
-
|
5
|
-
describe CultomePlayer::Player::Interface::Extended do
|
6
|
-
let(:t){ TestClass.new }
|
7
|
-
|
8
|
-
before :each do
|
9
|
-
Drive.create!(id: 1, name: 'test', path: '/patito', connected: true)
|
10
|
-
Song.create!(id: 1, name: "song_uno", artist_id: 1, album_id: 1, drive_id: 1, relative_path: "uno/uno.mp3")
|
11
|
-
Song.create!(id: 2, name: "song_dos", artist_id: 1, album_id: 2, drive_id: 1, relative_path: "uno/dos/dos.mp3")
|
12
|
-
Song.create!(id: 3, name: "song_tres", artist_id: 1, album_id: 2, drive_id: 1, relative_path: "uno/dos/tres/tres.mp3")
|
13
|
-
Song.create!(id: 4, name: "song_cuatro", artist_id: 2, album_id: 3, drive_id: 1, relative_path: "fake/cuatro.mp3")
|
14
|
-
Song.create!(id: 5, name: "song_cinco", artist_id: 3, album_id: 4, drive_id: 1, relative_path: "fake/cinco.mp3")
|
15
|
-
|
16
|
-
Artist.create!(id: 1, name: "artist_uno")
|
17
|
-
Artist.create!(id: 2, name: "artist_dos")
|
18
|
-
Artist.create!(id: 3, name: "artist_tres")
|
19
|
-
Artist.create!(id: 4, name: "artist_cuatro")
|
20
|
-
|
21
|
-
Album.create!(id: 1, name: "album_uno")
|
22
|
-
Album.create!(id: 2, name: "album_dos")
|
23
|
-
Album.create!(id: 3, name: "album_tres")
|
24
|
-
Album.create!(id: 4, name: "album_cuatro")
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'return a Response object' do
|
28
|
-
t.execute('search a:artist_uno').should be_instance_of Response
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'respond success when there are results' do
|
32
|
-
r = t.execute('search a:artist_uno')
|
33
|
-
r.should be_success
|
34
|
-
r.should respond_to :songs
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'respond failure when there are not results' do
|
38
|
-
r = t.execute('search a:nothing')
|
39
|
-
r.should be_failure
|
40
|
-
r.should respond_to :message
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'with criteria parameters' do
|
44
|
-
it 'different criterias create an AND filter' do
|
45
|
-
r = t.execute 'search a:artist_uno b:album_dos'
|
46
|
-
songs = r.send(r.response_type)
|
47
|
-
songs.should have(2).items
|
48
|
-
songs.each{|s| s.artist.name.should match /uno/ }
|
49
|
-
songs.each{|s| s.album.name.should match /dos/ }
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'same criterias create an OR filter' do
|
53
|
-
r = t.execute 'search a:artist_dos a:artist_tres'
|
54
|
-
songs = r.send(r.response_type)
|
55
|
-
songs.should have(2).songs
|
56
|
-
songs.each{|s| s.artist.name.should match /(dos|tres)/ }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
context 'with object parameters' do
|
61
|
-
it 'search using an object as criteria' do
|
62
|
-
r = t.execute 'search @artist'
|
63
|
-
songs = r.send(r.response_type)
|
64
|
-
songs.should have(3).item
|
65
|
-
songs.each{|s| s.artist.name.should eq t.current_artist.name }
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'create and OR filter more than one object' do
|
69
|
-
t.execute('search @artist @album').should have(4).songs
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'with literal parameters' do
|
74
|
-
it 'create an OR filter with the fields trackname, artist and album' do
|
75
|
-
t.execute('search song_cuatro song_cinco').should have(2).songs
|
76
|
-
t.execute('search song_uno song_dos').should have(2).songs
|
77
|
-
t.execute('search album_dos cinco').should have(3).songs
|
78
|
-
t.execute('search cinco').should have(1).songs
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
context 'with mixed parameters' do
|
83
|
-
it 'create an OR filter between each type' do
|
84
|
-
t.execute('search t:song_tres artist_tres @album').should have(3).songs
|
85
|
-
t.execute('search t:song_cuatro t:song_dos @album').should have(2).songs
|
86
|
-
t.execute('search song_dos song_uno').should have(2).songs
|
87
|
-
t.execute('search @artist song_tres').should have(3).songs
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|