1 {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
4 -- This module provides a way of verifying that the interface to the MPD
5 -- commands is correct. It does so by capturing the data flow between the
6 -- command and a dummy socket, checking the captured data against a set of
7 -- predefined values that are known to be correct. Of course, this does not
8 -- verify that the external behaviour is correct, it's simply a way of
9 -- catching silly mistakes and subtle bugs in the interface itself, without
10 -- having to actually send any requests to a real server.
12 module Commands
(main
) where
14 import Network
.MPD
.Commands
15 import Network
.MPD
.Core
(Response
, MPDError
(..))
16 import Network
.MPD
.StringConn
19 import Prelude
hiding (repeat)
22 main
= mapM_ (\(n
, f
) -> f
>>= \x
-> printf
"%-14s: %s\n" n x
) tests
23 where tests
= [("enableOutput", testEnableOutput
)
24 ,("disableOutput", testDisableOutput
)
25 ,("outputs", testOutputs
)
26 ,("update0", testUpdate0
)
27 ,("update1", testUpdate1
)
28 ,("updateMany", testUpdateMany
)
30 ,("find / complex query", testFindComplex
)
31 ,("list(Nothing)", testListNothing
)
32 ,("list(Just)", testListJust
)
33 ,("listAll", testListAll
)
34 ,("lsInfo", testLsInfo
)
35 ,("listAllInfo", testListAllInfo
)
36 ,("search", testSearch
)
40 ,("add_ / playlist", testAdd_pl
)
42 ,("clear / playlist", testClearPlaylist
)
43 ,("clear / current", testClearCurrent
)
44 ,("plChangesPosId 0", testPlChangesPosId_0
)
45 ,("plChangesPosId 1", testPlChangesPosId_1
)
46 ,("plChangesPosId wierd", testPlChangesPosId_Wierd
)
47 ,("currentSong(_)", testCurrentSongStopped
)
48 ,("currentSong(>)", testCurrentSongPlaying
)
49 ,("delete0", testDelete0
)
50 ,("delete1", testDelete1
)
51 ,("delete2", testDelete2
)
57 ,("rename", testRename
)
61 ,("shuffle", testShuffle
)
62 ,("playlistInfo0", testPlaylistInfo0
)
63 ,("playlistInfo / pos", testPlaylistInfoPos
)
64 ,("playlistInfo / id", testPlaylistInfoId
)
65 ,("listPlaylistInfo", testListPlaylistInfo
)
66 ,("listPlaylist", testListPlaylist
)
67 ,("playlist", testPlaylist
)
68 ,("plchanges", testPlChanges
)
69 ,("playlistFind", testPlaylistFind
)
70 ,("playlistSearch", testPlaylistSearch
)
71 ,("crossfade", testCrossfade
)
73 ,("play / pos", testPlayPos
)
74 ,("play / id", testPlayId
)
78 ,("previous", testPrevious
)
79 ,("seek / pos", testSeekPos
)
80 ,("seek / id", testSeekId
)
81 ,("seek / current", testSeekCur
)
82 ,("random", testRandom
)
83 ,("repeat", testRepeat
)
84 ,("setVolume", testSetVolume
)
85 ,("volume", testVolume
)
86 ,("clearError", testClearError
)
87 ,("commands", testCommands
)
88 ,("notCommands", testNotCommands
)
89 ,("tagTypes", testTagTypes
)
90 ,("urlHandlers", testUrlHandlers
)
91 ,("password", testPassword
)
94 ,("updateId0", testUpdateId0
)
95 ,("updateId1", testUpdateId1
)
96 ,("toggle / stop", testToggleStop
)
97 ,("toggle / play", testTogglePlay
)
98 ,("toggle / pause", testTogglePause
)
99 ,("addMany0", testAddMany0
)
100 ,("addMany1", testAddMany1
)
101 ,("deleteMany1", testDeleteMany1
)
102 ,("song parsing / incomplete track",
103 testSongParseIncompleteTrack
)
104 ,("song parsing / complete track",
105 testSongParseCompleteTrack
)
108 test a b c
= liftM (showResult b
) $ testMPD a b
(return Nothing
) c
110 test_ a b
= test a
(Right
()) b
112 showResult
:: (Show a
) => Response a
-> Result a
-> String
113 showResult _ Ok
= "passed"
114 showResult expectedResult
(Failure result mms
) =
116 concatMap (\(x
,y
) -> "\n expected request: " ++ show x
++
117 "\n actual request: " ++ show y
) mms
++
118 "\n expected result: " ++ show expectedResult
++
119 "\n actual result: " ++ show result
121 emptySong
= Song
{ sgArtist
= ""
133 , sgIndex
= Nothing
}
137 -- These tests are meant to expose problems with internal
141 -- Should handle track = 'X'.
142 testSongParseIncompleteTrack
=
143 test
[("find Artist \"Foo\"", Right
"file: dir/Foo-Bar.ogg\n\
146 (Right
[emptySong
{ sgTrack
= (1,1)
147 , sgFilePath
= "dir/Foo-Bar.ogg"
149 (find $ Query Artist
"Foo")
151 -- Should handle track = 'X/Y'.
152 testSongParseCompleteTrack
=
153 test
[("find Artist \"Foo\"", Right
"file: dir/Foo-Bar.ogg\n\
156 (Right
[emptySong
{ sgTrack
= (2,12)
157 , sgFilePath
= "dir/Foo-Bar.ogg"
159 (find $ Query Artist
"Foo")
165 testEnableOutput
= test_
[("enableoutput 1", Right
"OK")] (enableOutput
1)
167 testDisableOutput
= test_
[("disableoutput 1", Right
"OK")] (disableOutput
1)
170 test
[("outputs", Right
$ unlines ["outputid: 0"
171 ,"outputname: SoundCard0"
174 ,"outputname: SoundCard1"
177 (Right
[Device
{ dOutputID
= 0
178 , dOutputName
= "SoundCard0"
179 , dOutputEnabled
= True }
180 ,Device
{ dOutputID
= 1
181 , dOutputName
= "SoundCard1"
182 , dOutputEnabled
= False }])
185 testUpdate0
= test_
[("update", Right
"updating_db: 1\nOK")] (update
[])
188 test_
[("update \"foo\"", Right
"updating_db: 1\nOK")]
192 test_
[("command_list_begin\nupdate \"foo\"\nupdate \"bar\"\n\
193 \command_list_end", Right
"updating_db: 1\nOK")]
194 (update
["foo","bar"])
201 test
[("find Artist \"Foo\"", Right
"file: dir/Foo-Bar.ogg\n\
206 (Right
[Song
{ sgArtist
= "Foo"
209 , sgFilePath
= "dir/Foo-Bar.ogg"
220 (find (Query Artist
"Foo"))
223 test
[("find Artist \"Foo\" Album \"Bar\"",
224 Right
"file: dir/Foo/Bar/Baz.ogg\n\
229 (Right
[emptySong
{ sgFilePath
= "dir/Foo/Bar/Baz.ogg"
232 , sgTitle
= "Baz" }])
233 (find $ MultiQuery
[Query Artist
"Foo", Query Album
"Bar"])
236 test
[("list Title", Right
"Title: Foo\nTitle: Bar\nOK")]
237 (Right
["Foo", "Bar"])
241 test
[("list Title Artist \"Muzz\"", Right
"Title: Foo\nOK")]
243 (list Title
(Just
$ Query Artist
"Muzz"))
246 test
[("listall \"\"", Right
"directory: FooBand\n\
247 \directory: FooBand/album1\n\
248 \file: FooBand/album1/01 - songA.ogg\n\
249 \file: FooBand/album1/02 - songB.ogg\nOK")]
250 (Right
["FooBand/album1/01 - songA.ogg"
251 ,"FooBand/album1/02 - songB.ogg"])
255 test
[("lsinfo \"\"", Right
"directory: Foo\ndirectory: Bar\nOK")]
256 (Right
[Left
"Bar", Left
"Foo"])
260 test
[("listallinfo \"\"", Right
"directory: Foo\ndirectory: Bar\nOK")]
261 (Right
[Left
"Bar", Left
"Foo"])
265 test
[("search Artist \"oo\"", Right
"file: dir/Foo-Bar.ogg\n\
270 (Right
[Song
{ sgArtist
= "Foo"
273 , sgFilePath
= "dir/Foo-Bar.ogg"
284 (search
(Query Artist
"oo"))
287 test
[("count Title \"Foo\"", Right
"songs: 1\nplaytime: 60\nOK")]
289 (count
(Query Title
"Foo"))
296 test
[("add \"foo\"", Right
"OK"),
297 ("listall \"foo\"", Right
"file: Foo\nfile: Bar\nOK")]
298 (Right
["Foo", "Bar"])
301 testAdd_
= test_
[("add \"foo\"", Right
"OK")] (add_
"" "foo")
303 testAdd_pl
= test_
[("playlistadd \"foo\" \"bar\"", Right
"OK")]
307 test
[("addid \"dir/Foo-Bar.ogg\"", Right
"Id: 20\nOK")]
309 (addId
"dir/Foo-Bar.ogg")
311 testClearPlaylist
= test_
[("playlistclear \"foo\"", Right
"OK")]
314 testClearCurrent
= test_
[("clear", Right
"OK")] (clear
"")
316 testPlChangesPosId_0
=
317 test
[("plchangesposid 10", Right
"OK")]
321 testPlChangesPosId_1
=
322 test
[("plchangesposid 10", Right
"cpos: 0\nId: 20\nOK")]
323 (Right
[(Pos
0, ID
20)])
326 testPlChangesPosId_Wierd
=
327 test
[("plchangesposid 10", Right
"cpos: foo\nId: bar\nOK")]
328 (Left
$ Unexpected
"[(\"cpos\",\"foo\"),(\"Id\",\"bar\")]")
331 testCurrentSongStopped
=
332 test
[("status", Right
"repeat: 0\n\
335 \playlistlength: 0\n\
341 testCurrentSongPlaying
=
342 test
[("status", Right
"volume: 80\n\
346 \playlistlength: 21\n\
353 \audio: 44100:16:2\n\
355 ,("currentsong", Right
"file: dir/Foo-Bar.ogg\n\
360 (Right
. Just
$ Song
{ sgArtist
= "Foo"
363 , sgFilePath
= "dir/Foo-Bar.ogg"
376 testDelete0
= test_
[("delete 1", Right
"OK")] (delete "" (Pos
1))
378 testDelete1
= test_
[("deleteid 1", Right
"OK")] (delete "" (ID
1))
380 testDelete2
= test_
[("playlistdelete \"foo\" 1", Right
"OK")] (delete "foo" (Pos
1))
382 testLoad
= test_
[("load \"foo\"", Right
"OK")] (load
"foo")
384 testMove0
= test_
[("move 1 2", Right
"OK")] (move
"" (Pos
1) 2)
386 testMove1
= test_
[("moveid 1 2", Right
"OK")] (move
"" (ID
1) 2)
388 testMove2
= test_
[("playlistmove \"foo\" 1 2", Right
"OK")] (move
"foo" (Pos
1) 2)
390 testRm
= test_
[("rm \"foo\"", Right
"OK")] (rm
"foo")
392 testRename
= test_
[("rename \"foo\" \"bar\"", Right
"OK")] (rename
"foo" "bar")
394 testSave
= test_
[("save \"foo\"", Right
"OK")] (save
"foo")
396 testSwap0
= test_
[("swap 1 2", Right
"OK")] (swap
(Pos
1) (Pos
2))
398 testSwap1
= test_
[("swapid 1 2", Right
"OK")] (swap
(ID
1) (ID
2))
400 testShuffle
= test_
[("shuffle", Right
"OK")] shuffle
402 testPlaylistInfo0
= test
[("playlistinfo", Right
"file: dir/Foo-Bar.ogg\n\
407 (Right
[emptySong
{ sgFilePath
= "dir/Foo-Bar.ogg"
410 , sgTitle
= "Bar" }])
411 (playlistInfo Nothing
)
413 testPlaylistInfoPos
= test
[("playlistinfo 1", Right
"file: dir/Foo-Bar.ogg\n\
418 (Right
[emptySong
{ sgFilePath
= "dir/Foo-Bar.ogg"
421 , sgTitle
= "Bar" }])
422 (playlistInfo
. Just
$ Pos
1)
424 testPlaylistInfoId
= test
[("playlistid 1", Right
"file: dir/Foo-Bar.ogg\n\
429 (Right
[emptySong
{ sgFilePath
= "dir/Foo-Bar.ogg"
432 , sgTitle
= "Bar" }])
433 (playlistInfo
. Just
$ ID
1)
435 testListPlaylistInfo
= test
[("listplaylistinfo \"foo\""
436 ,Right
"file: dir/Foo-Bar.ogg\n\
441 (Right
[emptySong
{ sgFilePath
= "dir/Foo-Bar.ogg"
444 , sgTitle
= "Bar" }])
445 (listPlaylistInfo
"foo")
447 testListPlaylist
= test
[("listplaylist \"foo\""
448 ,Right
"file: dir/Foo-bar.ogg\n\
449 \file: dir/Quux-quuz.ogg\n\
451 (Right
["dir/Foo-bar.ogg", "dir/Quux-quuz.ogg"])
454 testPlaylist
= test
[("playlist"
458 (Right
[(Pos
1, "Foo.ogg")
459 ,(Pos
2, "Bar.ogg")])
462 testPlChanges
= test
[("plchanges 0"
463 ,Right
"file: foo/bar.ogg\n\
467 (Right
[emptySong
{ sgArtist
= "Foo"
469 , sgFilePath
= "foo/bar.ogg" }])
472 testPlaylistFind
= test
[("playlistfind Artist \"Foo\""
473 ,Right
"file: dir/Foo/Bar.ogg\n\
476 (Right
[emptySong
{ sgFilePath
= "dir/Foo/Bar.ogg"
477 , sgArtist
= "Foo" }])
478 (playlistFind
$ Query Artist
"Foo")
480 testPlaylistSearch
= test
[("playlistsearch Artist \"Foo\""
481 ,Right
"file: dir/Foo/Bar.ogg\n\
484 (Right
[emptySong
{ sgFilePath
= "dir/Foo/Bar.ogg"
485 , sgArtist
= "Foo" }])
486 (playlistSearch
$ Query Artist
"Foo")
492 testCrossfade
= test_
[("crossfade 0", Right
"OK")] (crossfade
0)
494 testPlay
= test_
[("play", Right
"OK")] (play Nothing
)
496 testPlayPos
= test_
[("play 1", Right
"OK")] (play
. Just
$ Pos
1)
498 testPlayId
= test_
[("playid 1", Right
"OK")] (play
. Just
$ ID
1)
500 testPause
= test_
[("pause 0", Right
"OK")] (pause
False)
502 testStop
= test_
[("stop", Right
"OK")] stop
504 testNext
= test_
[("next", Right
"OK")] next
506 testPrevious
= test_
[("previous", Right
"OK")] previous
508 testSeekPos
= test_
[("seek 1 10", Right
"OK")] (seek
(Just
$ Pos
1) 10)
510 testSeekId
= test_
[("seekid 1 10", Right
"OK")] (seek
(Just
$ ID
1) 10)
512 testSeekCur
= test_
[("status", Right
"state: play\n\
515 ,("seekid 1 10", Right
"OK")]
518 testRandom
= test_
[("random 0", Right
"OK")] (random False)
520 testRepeat
= test_
[("repeat 0", Right
"OK")] (repeat False)
522 testSetVolume
= test_
[("setvol 10", Right
"OK")] (setVolume
10)
524 testVolume
= test_
[("volume 10", Right
"OK")] (volume
10)
527 -- Miscellaneous commands
530 testClearError
= test_
[("clearerror", Right
"OK")] clearError
533 test
[("commands", Right
"command: foo\ncommand: bar")]
534 (Right
["foo", "bar"])
538 test
[("notcommands", Right
"command: foo\ncommand: bar")]
539 (Right
["foo", "bar"])
543 test
[("tagtypes", Right
"tagtype: foo\ntagtype: bar")]
544 (Right
["foo", "bar"])
548 test
[("urlhandlers", Right
"urlhandler: foo\nurlhandler: bar")]
549 (Right
["foo", "bar"])
552 testPassword
= test_
[("password foo", Right
"OK")] (password
"foo")
554 testPing
= test_
[("ping", Right
"OK")] ping
556 testStats
= test
[("stats", Right
"artists: 1\n\
564 (Right Stats
{ stsArtists
= 1, stsAlbums
= 1, stsSongs
= 1
565 , stsUptime
= 100, stsPlaytime
= 100, stsDbUpdate
= 10
566 , stsDbPlaytime
= 100 })
570 -- Extensions\/shortcuts
573 testUpdateId0
= test
[("update", Right
"updating_db: 1")]
577 testUpdateId1
= test
[("update \"foo\"", Right
"updating_db: 1")]
581 testTogglePlay
= test_
582 [("status", Right
"state: play")
583 ,("pause 1", Right
"OK")]
586 testToggleStop
= test_
587 [("status", Right
"state: stop")
588 ,("play", Right
"OK")]
591 testTogglePause
= test_
592 [("status", Right
"state: pause")
593 ,("play", Right
"OK")]
596 testAddMany0
= test_
[("add \"bar\"", Right
"OK")]
599 testAddMany1
= test_
[("playlistadd \"foo\" \"bar\"", Right
"OK")]
600 (addMany
"foo" ["bar"])
602 testDeleteMany1
= test_
[("playlistdelete \"foo\" 1", Right
"OK")]
603 (deleteMany
"foo" [Pos
1])