[gitconv @ Unit test for stats]
[libmpd-haskell.git] / tests / Commands.hs
blob364a2c94c0312252849cd1f28d2030cf1376a7ab
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 ,("list(Nothing)", testListNothing)
31 ,("list(Just)", testListJust)
32 ,("listAll", testListAll)
33 ,("lsInfo", testLsInfo)
34 ,("listAllInfo", testListAllInfo)
35 ,("search", testSearch)
36 ,("count", testCount)
37 ,("add", testAdd)
38 ,("add_", testAdd_)
39 ,("add_ / playlist", testAdd_pl)
40 ,("addId", testAddId)
41 ,("clear", testClear)
42 ,("plChangesPosId 0", testPlChangesPosId_0)
43 ,("plChangesPosId 1", testPlChangesPosId_1)
44 ,("plChangesPosId wierd", testPlChangesPosId_Wierd)
45 ,("currentSong(_)", testCurrentSongStopped)
46 ,("currentSong(>)", testCurrentSongPlaying)
47 ,("delete0", testDelete0)
48 ,("delete1", testDelete1)
49 ,("delete2", testDelete2)
50 ,("load", testLoad)
51 ,("move0", testMove0)
52 ,("move1", testMove1)
53 ,("move2", testMove2)
54 ,("rm", testRm)
55 ,("rename", testRename)
56 ,("save", testSave)
57 ,("swap0", testSwap0)
58 ,("swap1", testSwap1)
59 ,("shuffle", testShuffle)
60 ,("playlistInfo0", testPlaylistInfo0)
61 ,("playlistInfo / pos", testPlaylistInfoPos)
62 ,("playlistInfo / id", testPlaylistInfoId)
63 ,("listPlaylistInfo", testListPlaylistInfo)
64 ,("listPlaylist", testListPlaylist)
65 ,("playlist", testPlaylist)
66 ,("plchanges", testPlChanges)
67 ,("playlistFind", testPlaylistFind)
68 ,("playlistSearch", testPlaylistSearch)
69 ,("crossfade", testCrossfade)
70 ,("play", testPlay)
71 ,("play / pos", testPlayPos)
72 ,("play / id", testPlayId)
73 ,("pause", testPause)
74 ,("stop", testStop)
75 ,("next", testNext)
76 ,("previous", testPrevious)
77 ,("seek / pos", testSeekPos)
78 ,("seek / id", testSeekId)
79 ,("random", testRandom)
80 ,("repeat", testRepeat)
81 ,("setVolume", testSetVolume)
82 ,("volume", testVolume)
83 ,("clearError", testClearError)
84 ,("commands", testCommands)
85 ,("notCommands", testNotCommands)
86 ,("tagTypes", testTagTypes)
87 ,("urlHandlers", testUrlHandlers)
88 ,("password", testPassword)
89 ,("ping", testPing)
90 ,("stats", testStats)
91 ,("song parsing / incomplete track",
92 testSongParseIncompleteTrack)
93 ,("song parsing / complete track",
94 testSongParseCompleteTrack)
97 test a b c = liftM (showResult b) $ testMPD a b (return Nothing) c
99 test_ a b = test a (Right ()) b
101 showResult :: (Show a) => Response a -> Result a -> String
102 showResult _ Ok = "passed"
103 showResult expectedResult (Failure result mms) =
104 "*** FAILURE ***" ++
105 concatMap (\(x,y) -> "\n expected request: " ++ show x ++
106 "\n actual request: " ++ show y) mms ++
107 "\n expected result: " ++ show expectedResult ++
108 "\n actual result: " ++ show result
110 emptySong = Song { sgArtist = ""
111 , sgAlbum = ""
112 , sgTitle = ""
113 , sgFilePath = ""
114 , sgGenre = ""
115 , sgName = ""
116 , sgComposer = ""
117 , sgPerformer = ""
118 , sgLength = 0
119 , sgDate = 0
120 , sgTrack = (0,0)
121 , sgDisc = (0,0)
122 , sgIndex = Nothing }
125 -- Parser behaviour.
126 -- These tests are meant to expose problems with internal
127 -- parsers.
130 -- Should handle track = 'X'.
131 testSongParseIncompleteTrack =
132 test [("find Artist \"Foo\"", Right "file: dir/Foo-Bar.ogg\n\
133 \Track: 1\n\
134 \OK")]
135 (Right [emptySong { sgTrack = (1,1)
136 , sgFilePath = "dir/Foo-Bar.ogg"
138 (find $ Query Artist "Foo")
140 -- Should handle track = 'X/Y'.
141 testSongParseCompleteTrack =
142 test [("find Artist \"Foo\"", Right "file: dir/Foo-Bar.ogg\n\
143 \Track: 2/12\n\
144 \OK")]
145 (Right [emptySong { sgTrack = (2,12)
146 , sgFilePath = "dir/Foo-Bar.ogg"
148 (find $ Query Artist "Foo")
151 -- Admin commands
154 testEnableOutput = test_ [("enableoutput 1", Right "OK")] (enableOutput 1)
156 testDisableOutput = test_ [("disableoutput 1", Right "OK")] (disableOutput 1)
158 testOutputs =
159 test [("outputs", Right $ unlines ["outputid: 0"
160 ,"outputname: SoundCard0"
161 ,"outputenabled: 1"
162 ,"outputid: 1"
163 ,"outputname: SoundCard1"
164 ,"outputenabled: 0"
165 ,"OK"])]
166 (Right [Device { dOutputID = 0
167 , dOutputName = "SoundCard0"
168 , dOutputEnabled = True }
169 ,Device { dOutputID = 1
170 , dOutputName = "SoundCard1"
171 , dOutputEnabled = False }])
172 outputs
174 testUpdate0 = test_ [("update", Right "updating_db: 1\nOK")] (update [])
176 testUpdate1 =
177 test_ [("update \"foo\"", Right "updating_db: 1\nOK")]
178 (update ["foo"])
180 testUpdateMany =
181 test_ [("command_list_begin\nupdate \"foo\"\nupdate \"bar\"\n\
182 \command_list_end", Right "updating_db: 1\nOK")]
183 (update ["foo","bar"])
186 -- Database commands
189 testFind =
190 test [("find Artist \"Foo\"", Right "file: dir/Foo-Bar.ogg\n\
191 \Time: 60\n\
192 \Artist: Foo\n\
193 \Title: Bar\n\
194 \OK")]
195 (Right [Song { sgArtist = "Foo"
196 , sgAlbum = ""
197 , sgTitle = "Bar"
198 , sgFilePath = "dir/Foo-Bar.ogg"
199 , sgGenre = ""
200 , sgName = ""
201 , sgComposer = ""
202 , sgPerformer = ""
203 , sgLength = 60
204 , sgDate = 0
205 , sgTrack = (0,0)
206 , sgDisc = (0,0)
207 , sgIndex = Nothing
209 (find (Query Artist "Foo"))
211 testListNothing =
212 test [("list Title", Right "Title: Foo\nTitle: Bar\nOK")]
213 (Right ["Foo", "Bar"])
214 (list Title Nothing)
216 testListJust =
217 test [("list Title Artist \"Muzz\"", Right "Title: Foo\nOK")]
218 (Right ["Foo"])
219 (list Title (Just $ Query Artist "Muzz"))
221 testListAll =
222 test [("listall \"\"", Right "directory: FooBand\n\
223 \directory: FooBand/album1\n\
224 \file: FooBand/album1/01 - songA.ogg\n\
225 \file: FooBand/album1/02 - songB.ogg\nOK")]
226 (Right ["FooBand/album1/01 - songA.ogg"
227 ,"FooBand/album1/02 - songB.ogg"])
228 (listAll "")
230 testLsInfo =
231 test [("lsinfo \"\"", Right "directory: Foo\ndirectory: Bar\nOK")]
232 (Right [Left "Bar", Left "Foo"])
233 (lsInfo "")
235 testListAllInfo =
236 test [("listallinfo \"\"", Right "directory: Foo\ndirectory: Bar\nOK")]
237 (Right [Left "Bar", Left "Foo"])
238 (listAllInfo "")
240 testSearch =
241 test [("search Artist \"oo\"", Right "file: dir/Foo-Bar.ogg\n\
242 \Time: 60\n\
243 \Artist: Foo\n\
244 \Title: Bar\n\
245 \OK")]
246 (Right [Song { sgArtist = "Foo"
247 , sgAlbum = ""
248 , sgTitle = "Bar"
249 , sgFilePath = "dir/Foo-Bar.ogg"
250 , sgGenre = ""
251 , sgName = ""
252 , sgComposer = ""
253 , sgPerformer = ""
254 , sgLength = 60
255 , sgDate = 0
256 , sgTrack = (0,0)
257 , sgDisc = (0,0)
258 , sgIndex = Nothing
260 (search (Query Artist "oo"))
262 testCount =
263 test [("count Title \"Foo\"", Right "songs: 1\nplaytime: 60\nOK")]
264 (Right (Count 1 60))
265 (count (Query Title "Foo"))
268 -- Playlist commands
271 testAdd =
272 test [("add \"foo\"", Right "OK"),
273 ("listall \"foo\"", Right "file: Foo\nfile: Bar\nOK")]
274 (Right ["Foo", "Bar"])
275 (add "" "foo")
277 testAdd_ = test_ [("add \"foo\"", Right "OK")] (add_ "" "foo")
279 testAdd_pl = test_ [("playlistadd \"foo\" \"bar\"", Right "OK")]
280 (add_ "foo" "bar")
282 testAddId =
283 test [("addid \"dir/Foo-Bar.ogg\"", Right "Id: 20\nOK")]
284 (Right 20)
285 (addId "dir/Foo-Bar.ogg")
287 testClear = test_ [("playlistclear \"foo\"", Right "OK")] (clear "foo")
289 testPlChangesPosId_0 =
290 test [("plchangesposid 10", Right "OK")]
291 (Right [])
292 (plChangesPosId 10)
294 testPlChangesPosId_1 =
295 test [("plchangesposid 10", Right "cpos: 0\nId: 20\nOK")]
296 (Right [(Pos 0, ID 20)])
297 (plChangesPosId 10)
299 testPlChangesPosId_Wierd =
300 test [("plchangesposid 10", Right "cpos: foo\nId: bar\nOK")]
301 (Left $ Unexpected "[(\"cpos\",\"foo\"),(\"Id\",\"bar\")]")
302 (plChangesPosId 10)
304 testCurrentSongStopped =
305 test [("status", Right "repeat: 0\n\
306 \random: 0\n\
307 \playlist: 253\n\
308 \playlistlength: 0\n\
309 \xfade: 0\n\
310 \state: stop\nOK")]
311 (Right Nothing)
312 (currentSong)
314 testCurrentSongPlaying =
315 test [("status", Right "volume: 80\n\
316 \repeat: 0\n\
317 \random: 0\n\
318 \playlist: 252\n\
319 \playlistlength: 21\n\
320 \xfade: 0\n\
321 \state: play\n\
322 \song: 20\n\
323 \songid: 238\n\
324 \time: 158:376\n\
325 \bitrate: 192\n\
326 \audio: 44100:16:2\n\
327 \OK")
328 ,("currentsong", Right "file: dir/Foo-Bar.ogg\n\
329 \Time: 60\n\
330 \Artist: Foo\n\
331 \Title: Bar\n\
332 \OK")]
333 (Right . Just $ Song { sgArtist = "Foo"
334 , sgAlbum = ""
335 , sgTitle = "Bar"
336 , sgFilePath = "dir/Foo-Bar.ogg"
337 , sgGenre = ""
338 , sgName = ""
339 , sgComposer = ""
340 , sgPerformer = ""
341 , sgLength = 60
342 , sgDate = 0
343 , sgTrack = (0,0)
344 , sgDisc = (0,0)
345 , sgIndex = Nothing
347 (currentSong)
349 testDelete0 = test_ [("delete 1", Right "OK")] (delete "" (Pos 1))
351 testDelete1 = test_ [("deleteid 1", Right "OK")] (delete "" (ID 1))
353 testDelete2 = test_ [("playlistdelete \"foo\" 1", Right "OK")] (delete "foo" (Pos 1))
355 testLoad = test_ [("load \"foo\"", Right "OK")] (load "foo")
357 testMove0 = test_ [("move 1 2", Right "OK")] (move "" (Pos 1) 2)
359 testMove1 = test_ [("moveid 1 2", Right "OK")] (move "" (ID 1) 2)
361 testMove2 = test_ [("playlistmove \"foo\" 1 2", Right "OK")] (move "foo" (Pos 1) 2)
363 testRm = test_ [("rm \"foo\"", Right "OK")] (rm "foo")
365 testRename = test_ [("rename \"foo\" \"bar\"", Right "OK")] (rename "foo" "bar")
367 testSave = test_ [("save \"foo\"", Right "OK")] (save "foo")
369 testSwap0 = test_ [("swap 1 2", Right "OK")] (swap (Pos 1) (Pos 2))
371 testSwap1 = test_ [("swapid 1 2", Right "OK")] (swap (ID 1) (ID 2))
373 testShuffle = test_ [("shuffle", Right "OK")] shuffle
375 testPlaylistInfo0 = test [("playlistinfo", Right "file: dir/Foo-Bar.ogg\n\
376 \Time: 60\n\
377 \Artist: Foo\n\
378 \Title: Bar\n\
379 \OK")]
380 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
381 , sgLength = 60
382 , sgArtist = "Foo"
383 , sgTitle = "Bar" }])
384 (playlistInfo Nothing)
386 testPlaylistInfoPos = test [("playlistinfo 1", Right "file: dir/Foo-Bar.ogg\n\
387 \Time: 60\n\
388 \Artist: Foo\n\
389 \Title: Bar\n\
390 \OK")]
391 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
392 , sgLength = 60
393 , sgArtist = "Foo"
394 , sgTitle = "Bar" }])
395 (playlistInfo . Just $ Pos 1)
397 testPlaylistInfoId = test [("playlistid 1", Right "file: dir/Foo-Bar.ogg\n\
398 \Time: 60\n\
399 \Artist: Foo\n\
400 \Title: Bar\n\
401 \OK")]
402 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
403 , sgLength = 60
404 , sgArtist = "Foo"
405 , sgTitle = "Bar" }])
406 (playlistInfo . Just $ ID 1)
408 testListPlaylistInfo = test [("listplaylistinfo \"foo\""
409 ,Right "file: dir/Foo-Bar.ogg\n\
410 \Time: 60\n\
411 \Artist: Foo\n\
412 \Title: Bar\n\
413 \OK")]
414 (Right [emptySong { sgFilePath = "dir/Foo-Bar.ogg"
415 , sgLength = 60
416 , sgArtist = "Foo"
417 , sgTitle = "Bar" }])
418 (listPlaylistInfo "foo")
420 testListPlaylist = test [("listplaylist \"foo\""
421 ,Right "file: dir/Foo-bar.ogg\n\
422 \file: dir/Quux-quuz.ogg\n\
423 \OK")]
424 (Right ["dir/Foo-bar.ogg", "dir/Quux-quuz.ogg"])
425 (listPlaylist "foo")
427 testPlaylist = test [("playlist"
428 ,Right "1:Foo.ogg\n\
429 \2:Bar.ogg\n\
430 \OK")]
431 (Right [(Pos 1, "Foo.ogg")
432 ,(Pos 2, "Bar.ogg")])
433 playlist
435 testPlChanges = test [("plchanges 0"
436 ,Right "file: foo/bar.ogg\n\
437 \Artist: Foo\n\
438 \Title: Bar\n\
439 \OK")]
440 (Right [emptySong { sgArtist = "Foo"
441 , sgTitle = "Bar"
442 , sgFilePath = "foo/bar.ogg" }])
443 (plChanges 0)
445 testPlaylistFind = test [("playlistfind Artist \"Foo\""
446 ,Right "file: dir/Foo/Bar.ogg\n\
447 \Artist: Foo\n\
448 \OK")]
449 (Right [emptySong { sgFilePath = "dir/Foo/Bar.ogg"
450 , sgArtist = "Foo" }])
451 (playlistFind $ Query Artist "Foo")
453 testPlaylistSearch = test [("playlistsearch Artist \"Foo\""
454 ,Right "file: dir/Foo/Bar.ogg\n\
455 \Artist: Foo\n\
456 \OK")]
457 (Right [emptySong { sgFilePath = "dir/Foo/Bar.ogg"
458 , sgArtist = "Foo" }])
459 (playlistSearch $ Query Artist "Foo")
462 -- Playback commands
465 testCrossfade = test_ [("crossfade 0", Right "OK")] (crossfade 0)
467 testPlay = test_ [("play", Right "OK")] (play Nothing)
469 testPlayPos = test_ [("play 1", Right "OK")] (play . Just $ Pos 1)
471 testPlayId = test_ [("playid 1", Right "OK")] (play . Just $ ID 1)
473 testPause = test_ [("pause 0", Right "OK")] (pause False)
475 testStop = test_ [("stop", Right "OK")] stop
477 testNext = test_ [("next", Right "OK")] next
479 testPrevious = test_ [("previous", Right "OK")] previous
481 testSeekPos = test_ [("seek 1 10", Right "OK")] (seek (Just $ Pos 1) 10)
483 testSeekId = test_ [("seekid 1 10", Right "OK")] (seek (Just $ ID 1) 10)
485 testRandom = test_ [("random 0", Right "OK")] (random False)
487 testRepeat = test_ [("repeat 0", Right "OK")] (repeat False)
489 testSetVolume = test_ [("setvol 10", Right "OK")] (setVolume 10)
491 testVolume = test_ [("volume 10", Right "OK")] (volume 10)
494 -- Miscellaneous commands
497 testClearError = test_ [("clearerror", Right "OK")] clearError
499 testCommands =
500 test [("commands", Right "command: foo\ncommand: bar")]
501 (Right ["foo", "bar"])
502 commands
504 testNotCommands =
505 test [("notcommands", Right "command: foo\ncommand: bar")]
506 (Right ["foo", "bar"])
507 notCommands
509 testTagTypes =
510 test [("tagtypes", Right "tagtype: foo\ntagtype: bar")]
511 (Right ["foo", "bar"])
512 tagTypes
514 testUrlHandlers =
515 test [("urlhandlers", Right "urlhandler: foo\nurlhandler: bar")]
516 (Right ["foo", "bar"])
517 urlHandlers
519 testPassword = test_ [("password foo", Right "OK")] (password "foo")
521 testPing = test_ [("ping", Right "OK")] ping
523 testStats = test [("stats", Right "artists: 1\n\
524 \albums: 1\n\
525 \songs: 1\n\
526 \uptime: 100\n\
527 \playtime: 100\n\
528 \db_playtime: 100\n\
529 \db_update: 10\n\
530 \OK")]
531 (Right Stats { stsArtists = 1, stsAlbums = 1, stsSongs = 1
532 , stsUptime = 100, stsPlaytime = 100, stsDbUpdate = 10
533 , stsDbPlaytime = 100 })
534 stats
537 -- Extensions\/shortcuts