Add Displayable instance for Stats
[libmpd_haskell.git] / tests / Commands.hs
blob1c226400b4c9b3e8153f98f8ba561df27482f2bd
1 {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
3 -- |
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
18 import Control.Monad
19 import Prelude hiding (repeat)
20 import Text.Printf
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)
29 ,("find", testFind)
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)
37 ,("count", testCount)
38 ,("add", testAdd)
39 ,("add_", testAdd_)
40 ,("add_ / playlist", testAdd_pl)
41 ,("addId", testAddId)
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)
52 ,("load", testLoad)
53 ,("move0", testMove0)
54 ,("move1", testMove1)
55 ,("move2", testMove2)
56 ,("rm", testRm)
57 ,("rename", testRename)
58 ,("save", testSave)
59 ,("swap0", testSwap0)
60 ,("swap1", testSwap1)
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)
72 ,("play", testPlay)
73 ,("play / pos", testPlayPos)
74 ,("play / id", testPlayId)
75 ,("pause", testPause)
76 ,("stop", testStop)
77 ,("next", testNext)
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)
92 ,("ping", testPing)
93 ,("stats", testStats)
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) =
115 "*** FAILURE ***" ++
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 = ""
122 , sgAlbum = ""
123 , sgTitle = ""
124 , sgFilePath = ""
125 , sgGenre = ""
126 , sgName = ""
127 , sgComposer = ""
128 , sgPerformer = ""
129 , sgLength = 0
130 , sgDate = 0
131 , sgTrack = (0,0)
132 , sgDisc = (0,0)
133 , sgIndex = Nothing }
136 -- Parser behaviour.
137 -- These tests are meant to expose problems with internal
138 -- parsers.
141 -- Should handle track = 'X'.
142 testSongParseIncompleteTrack =
143 test [("find Artist \"Foo\"", Right "file: dir/Foo-Bar.ogg\n\
144 \Track: 1\n\
145 \OK")]
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\
154 \Track: 2/12\n\
155 \OK")]
156 (Right [emptySong { sgTrack = (2,12)
157 , sgFilePath = "dir/Foo-Bar.ogg"
159 (find $ Query Artist "Foo")
162 -- Admin commands
165 testEnableOutput = test_ [("enableoutput 1", Right "OK")] (enableOutput 1)
167 testDisableOutput = test_ [("disableoutput 1", Right "OK")] (disableOutput 1)
169 testOutputs =
170 test [("outputs", Right $ unlines ["outputid: 0"
171 ,"outputname: SoundCard0"
172 ,"outputenabled: 1"
173 ,"outputid: 1"
174 ,"outputname: SoundCard1"
175 ,"outputenabled: 0"
176 ,"OK"])]
177 (Right [Device { dOutputID = 0
178 , dOutputName = "SoundCard0"
179 , dOutputEnabled = True }
180 ,Device { dOutputID = 1
181 , dOutputName = "SoundCard1"
182 , dOutputEnabled = False }])
183 outputs
185 testUpdate0 = test_ [("update", Right "updating_db: 1\nOK")] (update [])
187 testUpdate1 =
188 test_ [("update \"foo\"", Right "updating_db: 1\nOK")]
189 (update ["foo"])
191 testUpdateMany =
192 test_ [("command_list_begin\nupdate \"foo\"\nupdate \"bar\"\n\
193 \command_list_end", Right "updating_db: 1\nOK")]
194 (update ["foo","bar"])
197 -- Database commands
200 testFind =
201 test [("find Artist \"Foo\"", Right "file: dir/Foo-Bar.ogg\n\
202 \Time: 60\n\
203 \Artist: Foo\n\
204 \Title: Bar\n\
205 \OK")]
206 (Right [Song { sgArtist = "Foo"
207 , sgAlbum = ""
208 , sgTitle = "Bar"
209 , sgFilePath = "dir/Foo-Bar.ogg"
210 , sgGenre = ""
211 , sgName = ""
212 , sgComposer = ""
213 , sgPerformer = ""
214 , sgLength = 60
215 , sgDate = 0
216 , sgTrack = (0,0)
217 , sgDisc = (0,0)
218 , sgIndex = Nothing
220 (find (Query Artist "Foo"))
222 testFindComplex =
223 test [("find Artist \"Foo\" Album \"Bar\"",
224 Right "file: dir/Foo/Bar/Baz.ogg\n\
225 \Artist: Foo\n\
226 \Album: Bar\n\
227 \Title: Baz\n\
228 \OK")]
229 (Right [emptySong { sgFilePath = "dir/Foo/Bar/Baz.ogg"
230 , sgArtist = "Foo"
231 , sgAlbum = "Bar"
232 , sgTitle = "Baz" }])
233 (find $ MultiQuery [Query Artist "Foo", Query Album "Bar"])
235 testListNothing =
236 test [("list Title", Right "Title: Foo\nTitle: Bar\nOK")]
237 (Right ["Foo", "Bar"])
238 (list Title Nothing)
240 testListJust =
241 test [("list Title Artist \"Muzz\"", Right "Title: Foo\nOK")]
242 (Right ["Foo"])
243 (list Title (Just $ Query Artist "Muzz"))
245 testListAll =
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"])
252 (listAll "")
254 testLsInfo =
255 test [("lsinfo \"\"", Right "directory: Foo\ndirectory: Bar\nOK")]
256 (Right [Left "Bar", Left "Foo"])
257 (lsInfo "")
259 testListAllInfo =
260 test [("listallinfo \"\"", Right "directory: Foo\ndirectory: Bar\nOK")]
261 (Right [Left "Bar", Left "Foo"])
262 (listAllInfo "")
264 testSearch =
265 test [("search Artist \"oo\"", Right "file: dir/Foo-Bar.ogg\n\
266 \Time: 60\n\
267 \Artist: Foo\n\
268 \Title: Bar\n\
269 \OK")]
270 (Right [Song { sgArtist = "Foo"
271 , sgAlbum = ""
272 , sgTitle = "Bar"
273 , sgFilePath = "dir/Foo-Bar.ogg"
274 , sgGenre = ""
275 , sgName = ""
276 , sgComposer = ""
277 , sgPerformer = ""
278 , sgLength = 60
279 , sgDate = 0
280 , sgTrack = (0,0)
281 , sgDisc = (0,0)
282 , sgIndex = Nothing
284 (search (Query Artist "oo"))
286 testCount =
287 test [("count Title \"Foo\"", Right "songs: 1\nplaytime: 60\nOK")]
288 (Right (Count 1 60))
289 (count (Query Title "Foo"))
292 -- Playlist commands
295 testAdd =
296 test [("add \"foo\"", Right "OK"),
297 ("listall \"foo\"", Right "file: Foo\nfile: Bar\nOK")]
298 (Right ["Foo", "Bar"])
299 (add "" "foo")
301 testAdd_ = test_ [("add \"foo\"", Right "OK")] (add_ "" "foo")
303 testAdd_pl = test_ [("playlistadd \"foo\" \"bar\"", Right "OK")]
304 (add_ "foo" "bar")
306 testAddId =
307 test [("addid \"dir/Foo-Bar.ogg\"", Right "Id: 20\nOK")]
308 (Right 20)
309 (addId "dir/Foo-Bar.ogg")
311 testClearPlaylist = test_ [("playlistclear \"foo\"", Right "OK")]
312 (clear "foo")
314 testClearCurrent = test_ [("clear", Right "OK")] (clear "")
316 testPlChangesPosId_0 =
317 test [("plchangesposid 10", Right "OK")]
318 (Right [])
319 (plChangesPosId 10)
321 testPlChangesPosId_1 =
322 test [("plchangesposid 10", Right "cpos: 0\nId: 20\nOK")]
323 (Right [(Pos 0, ID 20)])
324 (plChangesPosId 10)
326 testPlChangesPosId_Wierd =
327 test [("plchangesposid 10", Right "cpos: foo\nId: bar\nOK")]
328 (Left $ Unexpected "[(\"cpos\",\"foo\"),(\"Id\",\"bar\")]")
329 (plChangesPosId 10)
331 testCurrentSongStopped =
332 test [("status", Right "repeat: 0\n\
333 \random: 0\n\
334 \playlist: 253\n\
335 \playlistlength: 0\n\
336 \xfade: 0\n\
337 \state: stop\nOK")]
338 (Right Nothing)
339 (currentSong)
341 testCurrentSongPlaying =
342 test [("status", Right "volume: 80\n\
343 \repeat: 0\n\
344 \random: 0\n\
345 \playlist: 252\n\
346 \playlistlength: 21\n\
347 \xfade: 0\n\
348 \state: play\n\
349 \song: 20\n\
350 \songid: 238\n\
351 \time: 158:376\n\
352 \bitrate: 192\n\
353 \audio: 44100:16:2\n\
354 \OK")
355 ,("currentsong", Right "file: dir/Foo-Bar.ogg\n\
356 \Time: 60\n\
357 \Artist: Foo\n\
358 \Title: Bar\n\
359 \OK")]
360 (Right . Just $ Song { sgArtist = "Foo"
361 , sgAlbum = ""
362 , sgTitle = "Bar"
363 , sgFilePath = "dir/Foo-Bar.ogg"
364 , sgGenre = ""
365 , sgName = ""
366 , sgComposer = ""
367 , sgPerformer = ""
368 , sgLength = 60
369 , sgDate = 0
370 , sgTrack = (0,0)
371 , sgDisc = (0,0)
372 , sgIndex = Nothing
374 (currentSong)
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\
403 \Time: 60\n\
404 \Artist: Foo\n\
405 \Title: Bar\n\
406 \OK")]
407 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
408 , sgLength = 60
409 , sgArtist = "Foo"
410 , sgTitle = "Bar" }])
411 (playlistInfo Nothing)
413 testPlaylistInfoPos = test [("playlistinfo 1", Right "file: dir/Foo-Bar.ogg\n\
414 \Time: 60\n\
415 \Artist: Foo\n\
416 \Title: Bar\n\
417 \OK")]
418 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
419 , sgLength = 60
420 , sgArtist = "Foo"
421 , sgTitle = "Bar" }])
422 (playlistInfo . Just $ Pos 1)
424 testPlaylistInfoId = test [("playlistid 1", Right "file: dir/Foo-Bar.ogg\n\
425 \Time: 60\n\
426 \Artist: Foo\n\
427 \Title: Bar\n\
428 \OK")]
429 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
430 , sgLength = 60
431 , sgArtist = "Foo"
432 , sgTitle = "Bar" }])
433 (playlistInfo . Just $ ID 1)
435 testListPlaylistInfo = test [("listplaylistinfo \"foo\""
436 ,Right "file: dir/Foo-Bar.ogg\n\
437 \Time: 60\n\
438 \Artist: Foo\n\
439 \Title: Bar\n\
440 \OK")]
441 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
442 , sgLength = 60
443 , sgArtist = "Foo"
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\
450 \OK")]
451 (Right ["dir/Foo-bar.ogg", "dir/Quux-quuz.ogg"])
452 (listPlaylist "foo")
454 testPlaylist = test [("playlist"
455 ,Right "1:Foo.ogg\n\
456 \2:Bar.ogg\n\
457 \OK")]
458 (Right [(Pos 1, "Foo.ogg")
459 ,(Pos 2, "Bar.ogg")])
460 playlist
462 testPlChanges = test [("plchanges 0"
463 ,Right "file: foo/bar.ogg\n\
464 \Artist: Foo\n\
465 \Title: Bar\n\
466 \OK")]
467 (Right [emptySong { sgArtist = "Foo"
468 , sgTitle = "Bar"
469 , sgFilePath = "foo/bar.ogg" }])
470 (plChanges 0)
472 testPlaylistFind = test [("playlistfind Artist \"Foo\""
473 ,Right "file: dir/Foo/Bar.ogg\n\
474 \Artist: Foo\n\
475 \OK")]
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\
482 \Artist: Foo\n\
483 \OK")]
484 (Right [emptySong { sgFilePath = "dir/Foo/Bar.ogg"
485 , sgArtist = "Foo" }])
486 (playlistSearch $ Query Artist "Foo")
489 -- Playback commands
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\
513 \songid: 1\n\
514 \OK")
515 ,("seekid 1 10", Right "OK")]
516 (seek Nothing 10)
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
532 testCommands =
533 test [("commands", Right "command: foo\ncommand: bar")]
534 (Right ["foo", "bar"])
535 commands
537 testNotCommands =
538 test [("notcommands", Right "command: foo\ncommand: bar")]
539 (Right ["foo", "bar"])
540 notCommands
542 testTagTypes =
543 test [("tagtypes", Right "tagtype: foo\ntagtype: bar")]
544 (Right ["foo", "bar"])
545 tagTypes
547 testUrlHandlers =
548 test [("urlhandlers", Right "urlhandler: foo\nurlhandler: bar")]
549 (Right ["foo", "bar"])
550 urlHandlers
552 testPassword = test_ [("password foo", Right "OK")] (password "foo")
554 testPing = test_ [("ping", Right "OK")] ping
556 testStats = test [("stats", Right "artists: 1\n\
557 \albums: 1\n\
558 \songs: 1\n\
559 \uptime: 100\n\
560 \playtime: 100\n\
561 \db_playtime: 100\n\
562 \db_update: 10\n\
563 \OK")]
564 (Right Stats { stsArtists = 1, stsAlbums = 1, stsSongs = 1
565 , stsUptime = 100, stsPlaytime = 100, stsDbUpdate = 10
566 , stsDbPlaytime = 100 })
567 stats
570 -- Extensions\/shortcuts
573 testUpdateId0 = test [("update", Right "updating_db: 1")]
574 (Right 1)
575 (updateId [])
577 testUpdateId1 = test [("update \"foo\"", Right "updating_db: 1")]
578 (Right 1)
579 (updateId ["foo"])
581 testTogglePlay = test_
582 [("status", Right "state: play")
583 ,("pause 1", Right "OK")]
584 toggle
586 testToggleStop = test_
587 [("status", Right "state: stop")
588 ,("play", Right "OK")]
589 toggle
591 testTogglePause = test_
592 [("status", Right "state: pause")
593 ,("play", Right "OK")]
594 toggle
596 testAddMany0 = test_ [("add \"bar\"", Right "OK")]
597 (addMany "" ["bar"])
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])