music_blender 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +67 -0
  4. data/ID3TAGS.txt +26 -0
  5. data/LICENSE +20 -0
  6. data/README.md +41 -0
  7. data/Rakefile +27 -0
  8. data/bin/blend +5 -0
  9. data/db/migrate/001_create_root_folders.rb +10 -0
  10. data/db/migrate/002_create_tracks.rb +15 -0
  11. data/db/migrate/003_rename_root_folders_table_to_music_folders.rb +6 -0
  12. data/db/migrate/004_create_artists.rb +11 -0
  13. data/db/migrate/005_add_missing_column_to_tracks.rb +5 -0
  14. data/db/schema.rb +47 -0
  15. data/lib/music_blender.rb +26 -0
  16. data/lib/music_blender/artist.rb +6 -0
  17. data/lib/music_blender/bootstrap.rb +32 -0
  18. data/lib/music_blender/db_adapter.rb +58 -0
  19. data/lib/music_blender/id3_adapter.rb +70 -0
  20. data/lib/music_blender/music_folder.rb +51 -0
  21. data/lib/music_blender/player.rb +83 -0
  22. data/lib/music_blender/player_monitor.rb +77 -0
  23. data/lib/music_blender/shell.rb +59 -0
  24. data/lib/music_blender/track.rb +68 -0
  25. data/lib/music_blender/version.rb +3 -0
  26. data/music_blender.gemspec +28 -0
  27. data/test/factories/artists.rb +7 -0
  28. data/test/factories/music_folders.rb +15 -0
  29. data/test/factories/tracks.rb +12 -0
  30. data/test/music/point1sec.mp3 +0 -0
  31. data/test/music/subfolder/insubfolder.mp3 +0 -0
  32. data/test/music/test1.txt +0 -0
  33. data/test/music/test2.txt +0 -0
  34. data/test/test_helper.rb +53 -0
  35. data/test/unit/artist_test.rb +9 -0
  36. data/test/unit/bootstrap_test.rb +32 -0
  37. data/test/unit/db_adapter_test.rb +37 -0
  38. data/test/unit/id3_adapter_test.rb +42 -0
  39. data/test/unit/music_folder_test.rb +92 -0
  40. data/test/unit/player_monitor_test.rb +70 -0
  41. data/test/unit/player_test.rb +76 -0
  42. data/test/unit/shell_test.rb +71 -0
  43. data/test/unit/track_test.rb +48 -0
  44. metadata +250 -0
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class ArtistTest < MiniTest::Unit::TestCase
5
+ def test_instantiation
6
+ assert_kind_of(Artist,create(:artist))
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class BootstrapTest < MiniTest::Unit::TestCase
5
+ attr_reader :bootstrap, :mock_shell
6
+
7
+ describe Bootstrap do
8
+ let(:bootstrap) { Bootstrap.new }
9
+ let(:mock_shell) { mock('shell')}
10
+ let(:mock_music_folder) { mock('music_folder') }
11
+
12
+ before do
13
+ Shell.stubs(:new).returns(mock_shell)
14
+ MusicFolder.stubs(:current).returns(mock_music_folder)
15
+ end
16
+
17
+ def test_call
18
+ DbAdapter.any_instance.expects(:spin_up)
19
+ mock_music_folder.expects(:load_tracks)
20
+ mock_music_folder.expects(:update_missing_flags)
21
+ mock_shell.expects(:run).throws(:exited)
22
+ bootstrap.call
23
+ end
24
+
25
+ def test_config
26
+ assert_equal(mock_music_folder,bootstrap.send(:music_folder))
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class DbAdapterTest < MiniTest::Unit::TestCase
5
+
6
+ describe DbAdapter do
7
+ let(:mock_connection) { mock('db_connection') }
8
+ let(:adapter) { DbAdapter.new }
9
+
10
+ before do
11
+ ActiveRecord::Base.stubs(:establish_connection).returns(mock_connection)
12
+ ActiveRecord::Base.stubs(:connection).returns(mock_connection)
13
+ ActiveRecord::Base.stubs('logger').returns(true)
14
+ end
15
+
16
+ def test_spinup_without_prior_db
17
+ File.expects(:exist?).returns(false)
18
+ adapter.expects(:load)
19
+ ActiveRecord::Migrator.expects(:migrate).never
20
+
21
+ adapter.spin_up
22
+ end
23
+
24
+ def test_spinup_with_prior_db
25
+ File.expects(:exist?).returns(true)
26
+ ActiveRecord::Migrator.expects(:migrate)
27
+ File.expects(:open).with(adapter.send(:path_to_schema),'w:utf-8').yields(mock('schema_file'))
28
+ ActiveRecord::SchemaDumper.expects(:dump)
29
+ adapter.expects(:load).never
30
+
31
+ adapter.spin_up
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class Id3AdapterTest < MiniTest::Unit::TestCase
5
+
6
+ describe Id3Adapter do
7
+ let(:path) { "#{MUSIC_PATH}/point1sec.mp3" }
8
+ let(:adapter) { Id3Adapter.new(path,nil) }
9
+
10
+ describe 'loading data' do
11
+
12
+ def test_loads_artist
13
+ assert_equal('DefBeats', adapter.artist)
14
+ end
15
+
16
+ def test_loads_title
17
+ assert_equal('Silent MP3 10th-of-a-sec', adapter.title)
18
+ end
19
+
20
+ def test_loads_rating
21
+ assert_equal(1, adapter.rating)
22
+ end
23
+ end
24
+
25
+ describe 'setting data' do
26
+
27
+ def test_sets_rating
28
+ assert_difference('adapter.rating' => 41) do
29
+ adapter.set_rating(42)
30
+ end
31
+ ensure
32
+ adapter.send(:v2tag).remove_frame(adapter.send(:rating_frame))
33
+ adapter.send(:tag_file).save
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+
@@ -0,0 +1,92 @@
1
+ require 'test_helper'
2
+ require 'ostruct'
3
+
4
+ module MusicBlender
5
+ class MusicFolderTest < MiniTest::Unit::TestCase
6
+
7
+ describe MusicFolder do
8
+ let(:music_folder) { create(:music_folder_with_tracks) }
9
+ let(:mock_id3_tag) { mock('tag') }
10
+
11
+ before do
12
+ Track.any_instance.stubs(:rating_frame => OpenStruct.new(:text => '5'))
13
+ mock_id3_tag.stubs(:title => 'Foo', :artist => 'Bar')
14
+ Track.any_instance.stubs(:import_id3_tag_attributes)
15
+ end
16
+
17
+ def test_pick_a_track
18
+ assert_includes(music_folder.tracks,music_folder.pick_a_track)
19
+ end
20
+
21
+ describe 'loading new tracks' do
22
+
23
+ before do
24
+ Track.any_instance.unstub(:import_id3_tag_attributes)
25
+ create(:track, :music_folder => music_folder, :relative_path => 'a')
26
+ music_folder.stubs(:relative_paths => ['a','b','c'])
27
+ end
28
+
29
+ def test_load_new_tracks
30
+ assert_difference('Track.count' => 2) do
31
+ music_folder.load_tracks
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ describe 'update missing flags' do
38
+ let(:track) { create(:track) }
39
+
40
+ def test_updates_missing_flags
41
+ refute(track.missing?)
42
+ track.music_folder.update_missing_flags
43
+ assert(track.reload.missing?)
44
+ end
45
+ end
46
+
47
+ describe 'music_path' do
48
+
49
+ def test_uses_constant
50
+ assert_equal(MUSIC_PATH, MusicFolder.music_path)
51
+ end
52
+
53
+ end
54
+
55
+ describe 'current' do
56
+
57
+ before do
58
+ MusicFolder.instance_variable_set(:@current, nil)
59
+ end
60
+
61
+ describe 'when one does not already exist' do
62
+
63
+ before do
64
+ MusicFolder.stubs(:music_path).returns('/some/non/existent/path/dsedfget')
65
+ end
66
+
67
+ def test_create_music_folder
68
+ assert_difference('MusicFolder.count') do
69
+ MusicFolder.current
70
+ end
71
+ end
72
+
73
+ end
74
+
75
+ describe 'when one does already exist' do
76
+ let(:music_folder) { create(:music_folder) }
77
+
78
+ before do
79
+ MusicFolder.stubs(:music_path).returns(music_folder.path)
80
+ end
81
+
82
+ def test_fetch_current_music_folder
83
+ assert_difference('MusicFolder.count' => 0) do
84
+ MusicFolder.current
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+ require 'tempfile'
3
+
4
+ module MusicBlender
5
+ class PlayerMonitorTest < MiniTest::Unit::TestCase
6
+
7
+ describe PlayerMonitor do
8
+ attr_reader :mock_stderr_writable, :mock_stdout_writable, :thread
9
+
10
+ let (:mock_player) { mock('player') }
11
+ let (:monitor) { PlayerMonitor.new(mock_player) }
12
+
13
+ before do
14
+ mock_player.stubs(:logger => Logger.new('/dev/null'))
15
+ mock_stdout_readable, @mock_stdout_writable = IO.pipe
16
+ mock_stderr_readable, @mock_stderr_writable = IO.pipe
17
+ mock_player.stubs(:stdout).returns(mock_stdout_readable)
18
+ mock_player.stubs(:stderr).returns(mock_stderr_readable)
19
+ @thread = Thread.new { monitor.run }
20
+ end
21
+
22
+ after do
23
+ thread.exit
24
+ end
25
+
26
+ def test_should_process_i_messages
27
+ assert_equal(nil,monitor.song_name)
28
+ write_to_player('@I Foobar')
29
+ assert_equal('Foobar',monitor.song_name)
30
+ end
31
+
32
+ def test_should_process_f_messages
33
+ write_to_player('@F 1 2 3 4')
34
+ assert_equal(1,monitor.frames)
35
+ assert_equal(2,monitor.frames_remaining)
36
+ assert_equal(3,monitor.seconds)
37
+ assert_equal(4,monitor.seconds_remaining)
38
+ end
39
+
40
+ def test_should_process_p_stop_messages
41
+ write_to_player('@P 0')
42
+ assert_equal(0,monitor.stop_pause_status)
43
+ assert_equal(false,monitor.playing)
44
+ end
45
+
46
+ def test_should_process_p_paused_messages
47
+ write_to_player('@P 1')
48
+ assert_equal(1,monitor.stop_pause_status)
49
+ assert_equal(false,monitor.playing)
50
+ end
51
+
52
+ def test_should_process_p_resumed_messages
53
+ write_to_player('@P 2')
54
+ assert_equal(2,monitor.stop_pause_status)
55
+ assert_equal(true,monitor.playing)
56
+ end
57
+
58
+ #######
59
+ private
60
+ #######
61
+
62
+ def write_to_player(string)
63
+ mock_stdout_writable.write(string)
64
+ mock_stdout_writable.close
65
+ sleep 0.002
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class PlayerTest < MiniTest::Unit::TestCase
5
+
6
+ describe Player do
7
+ let(:player) { Player.new }
8
+ let(:mock_stdin) { mock('stdin') }
9
+ let(:mock_stdout) { mock('stdout') }
10
+ let(:mock_stderr) { mock('stderr') }
11
+ let(:mock_wait_thread) { mock('wait_thread') }
12
+ let(:mock_monitor_thread) { mock('monitor_thread') }
13
+
14
+ before do
15
+ Open3.expects(:popen3).returns(mock_stdin,mock_stdout,mock_stderr,mock_wait_thread)
16
+ Thread.expects(:new).returns(mock_monitor_thread)
17
+ player.stubs(:config)
18
+ end
19
+
20
+ describe 'play' do
21
+ let(:track1) { create(:track) }
22
+ let(:track2) { create(:track) }
23
+ let(:music_folder) { create(:music_folder) }
24
+
25
+ before do
26
+ player.stubs(:music_folder).returns(music_folder)
27
+ music_folder.stubs(:pick_a_track).returns(track1)
28
+ mock_stdin.stubs(:puts)
29
+ end
30
+
31
+ def test_loads_a_song
32
+ mock_stdin.expects(:puts).with(regexp_matches(/^LOAD #{track1.full_path}/))
33
+ player.play
34
+ end
35
+
36
+ def test_sets_last_played_at
37
+ player.stubs(:current_track => track1)
38
+ assert_operator(track1.last_played_at,:<,1.minute.ago)
39
+ player.play
40
+ assert_operator(track1.last_played_at,:>,1.minute.ago)
41
+ end
42
+
43
+ end
44
+
45
+ def test_should_pause
46
+ mock_stdin.expects(:puts).with('PAUSE')
47
+ player.pause
48
+ end
49
+
50
+ def test_should_stop
51
+ mock_stdin.expects(:puts).with('STOP')
52
+ player.stop
53
+ end
54
+
55
+ def test_should_quit
56
+ mock_stdin.expects(:puts).with('QUIT')
57
+ player.quit
58
+ end
59
+
60
+ describe 'delegated methods' do
61
+ let(:mock_monitor) { mock('monitor') }
62
+
63
+ before do
64
+ PlayerMonitor.stubs(:new => mock_monitor)
65
+ end
66
+
67
+ def test_should_get_status_string
68
+ mock_monitor.expects(:stop_pause_status => 1)
69
+ assert_equal('Paused', player.status_string)
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,71 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class ShellTest < MiniTest::Unit::TestCase
5
+ attr_reader :mock_player, :mock_scanner
6
+
7
+ describe Shell do
8
+ let(:mock_player) { mock('player') }
9
+ let(:shell) { Shell.new }
10
+
11
+ before do
12
+ mock_player.stubs(:stop)
13
+ mock_player.stubs(:quit)
14
+ Shell.stubs(:player).returns(mock_player)
15
+ end
16
+
17
+ def test_gracefully_fail_to_execute_command
18
+ assert_equal('',$stdout.string)
19
+ run_shell(:ping)
20
+ assert_match(/Unrecognized Command: ping/,$stdout.string)
21
+ end
22
+
23
+ describe 'info' do
24
+ before do
25
+ Shell.expects(:print_info)
26
+ end
27
+
28
+ def test_execute_info
29
+ assert_equal('',$stdout.string)
30
+ run_shell(:info)
31
+ refute_equal('',$stdout.string)
32
+ end
33
+ end
34
+
35
+ describe 'player commands' do
36
+ def test_execute_player_commands
37
+ [:play,:stop,:quit].each do |command|
38
+ mock_player.expects(command)
39
+ run_shell(command)
40
+ end
41
+ end
42
+ end
43
+
44
+ describe 'commands with arguments' do
45
+ let(:track) { create(:track) }
46
+
47
+ before do
48
+ mock_player.stubs(:current_track => track)
49
+ end
50
+
51
+ def test_executes_command_with_arguments
52
+ assert_equal('',$stdout.string)
53
+ assert_difference('track.rating' => -31) do
54
+ run_shell('rate 11')
55
+ end
56
+ assert_match(/Rating Updated To: 11/,$stdout.string)
57
+ end
58
+ end
59
+
60
+ #######
61
+ private
62
+ #######
63
+
64
+ def run_shell(command)
65
+ shell.stubs(:gets).returns(command.to_s).then.returns('exit')
66
+ assert_throws(:exited) { shell.run }
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,48 @@
1
+ require 'test_helper'
2
+
3
+ module MusicBlender
4
+ class TrackTest < MiniTest::Unit::TestCase
5
+
6
+ describe Track do
7
+ let(:track) { create(:track) }
8
+ let(:mock_id3_tag) { mock('tag') }
9
+
10
+ def setup
11
+ Track.any_instance.stubs(:rating_frame => OpenStruct.new(:text => '5'))
12
+ mock_id3_tag.stubs(:title => 'Foo', :artist => 'Bar')
13
+ Track.any_instance.stubs(:id3v2_tag => mock_id3_tag)
14
+ end
15
+
16
+ def test_full_path
17
+ assert_kind_of(String,track.full_path)
18
+ end
19
+
20
+
21
+ describe 'except_recently_played scope' do
22
+ attr_reader :track1, :track2, :track3
23
+
24
+ before do
25
+ @track1 = create(:track, :last_played_at => 3.days.ago)
26
+ @track2 = create(:track, :last_played_at => 2.days.ago)
27
+ @track3 = create(:track, :last_played_at => 1.days.ago)
28
+ end
29
+
30
+ def test_does_not_include_recently_played
31
+ assert_includes(Track.except_recently_played,track1)
32
+ track1.update_column(:last_played_at, Time.now)
33
+ refute_includes(Track.except_recently_played,track1)
34
+ end
35
+
36
+ describe 'most_recent scope' do
37
+
38
+ def test_gets_most_recent_2_tracks
39
+ refute_includes(Track.most_recent(2),track1)
40
+ track1.update_column(:last_played_at, Time.now)
41
+ assert_includes(Track.most_recent(2),track1)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end