module Billboard.Tests ( mainTestFile
, mainTestDir
, oddBeatLengthTest
, reduceTest
, reduceTestVerb
, rangeTest
, getOffBeats) where
import Test.HUnit
import Control.Monad (void)
import Data.List (genericLength)
import HarmTrace.Base.MusicTime (TimedData, onset, offset, getData)
import HarmTrace.Base.MusicRep (Chord (..))
import Billboard.BillboardParser ( parseBillboard)
import Billboard.BillboardData ( BillboardData (..), BBChord (..), isNoneBBChord
, reduceTimedBBChords, expandTimedBBChords
)
import Billboard.IOUtils
testBeatDeviationMultiplier :: Double
testBeatDeviationMultiplier = 0.075
mainTestFile :: (BillboardData -> IO Test) -> FilePath -> IO ()
mainTestFile testf fp =
do readFile fp >>= return . fst . parseBillboard
>>= testf
>>= void . runTestTT
mainTestDir :: ((BillboardData, Int) -> Test )-> FilePath -> IO ()
mainTestDir t fp = getBBFiles fp >>= mapM readParse >>=
void . runTestTT . applyTestToList t where
readParse :: (FilePath, Int) -> IO (BillboardData, Int)
readParse (f,i) = readFile f >>= return . (,i) . fst . parseBillboard
oddBeatLengthTest :: (BillboardData, Int) -> Test
oddBeatLengthTest (bbd, bbid) =
let song = filter (not . isNoneBBChord . getData ) . getSong $ bbd
(_, minLen, maxLen) = getMinMaxBeatLen song
in TestCase (assertBool ("odd Beat length detected for:\n"
++ show bbid ++ ": " ++ getTitle bbd)
(and . map (rangeCheck minLen maxLen) $ song))
rangeTest :: BillboardData -> IO Test
rangeTest d = do let (avgL, minL, maxL) = getMinMaxBeatLen . getSong $ d
putStrLn ("average beat length: " ++ show avgL)
return . applyTestToList (testf minL maxL) . getSong $ d where
testf :: Double -> Double -> TimedData BBChord -> Test
testf minLen maxLen t =
TestCase (assertBool ("Odd Beat length detected for:\n" ++ showChord t)
(rangeCheck minLen maxLen t))
showChord :: TimedData BBChord -> String
showChord t = (show . chord . getData $ t) ++ ", length: "
++ (show . beatDuration $ t) ++ " @ " ++ (show . onset $ t)
rangeCheck :: Double -> Double -> TimedData BBChord -> Bool
rangeCheck minLen maxLen t = let len = beatDuration t
in (len >= minLen && len <= maxLen) ||
(isNoneBBChord . getData $ t)
getMinMaxBeatLen :: [TimedData BBChord] -> (Double, Double, Double)
getMinMaxBeatLen = getMinMaxBeatLen' testBeatDeviationMultiplier
getMinMaxBeatLen' :: Double -> [TimedData BBChord] -> (Double, Double, Double)
getMinMaxBeatLen' mult song =
let chds = filter (not . isNoneBBChord . getData ) song
avgLen = (sum $ map beatDuration chds) / (genericLength chds)
in ( avgLen
, avgLen * mult
, avgLen * (mult + 1))
reduceTest :: (BillboardData, Int) -> Test
reduceTest (bbd, i) =
let cs = getSong bbd
in TestCase
(assertBool ("reduce mismatch for id " ++ show i)
(and $ zipWith (\a b -> getData a `bbChordEq` getData b)
(expandTimedBBChords . reduceTimedBBChords $ cs) cs))
reduceTestVerb :: BillboardData -> IO Test
reduceTestVerb bbd =
do let cs = getSong bbd
return . applyTestToList cTest $
zip (expandTimedBBChords . reduceTimedBBChords $ cs) cs where
cTest :: (TimedData BBChord, TimedData BBChord) -> Test
cTest (a,b) = TestCase
(assertBool ("non-maching chords: " ++ show a ++ " and " ++ show b)
(getData a `bbChordEq` getData b))
bbChordEq :: BBChord -> BBChord -> Bool
bbChordEq (BBChord anA btA cA) (BBChord anB btB cB) =
anA == anB && btA == btB &&
chordRoot cA == chordRoot cB &&
chordShorthand cA == chordShorthand cB &&
chordAdditions cA == chordAdditions cB
getOffBeats :: Double -> [TimedData BBChord] -> [TimedData BBChord]
getOffBeats th song = let (_avg, mn, mx) = getMinMaxBeatLen' th song
in filter (not . rangeCheck mn mx) song
beatDuration :: TimedData a -> Double
beatDuration t = offset t onset t
applyTestToList :: (a -> Test) -> [a] -> Test
applyTestToList testf a = TestList (map testf a)