{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Dahdit (Binary (..), ByteCount (..), GetError, StaticByteSized, decodeFileEnd)
import Dahdit.Midi.Binary
( MidiInt14
, MidiInt7
, MidiWord14
, MidiWord7
, VarWord
)
import Dahdit.Midi.Midi qualified as MM
import Dahdit.Midi.Osc qualified as MO
import Dahdit.Midi.OscAddr qualified as MOA
import Data.ByteString (ByteString)
import Data.ByteString.Char8 qualified as BSC
import Data.Proxy (Proxy (..))
import Data.Sequence qualified as Seq
import Data.Word (Word8)
import System.Directory (listDirectory)
import System.FilePath (takeExtension, (</>))
import Test.Dahdit.Daytripper (expectBytes, expectCodecErr, expectCodecOk, expectStatic)
import Test.Dahdit.Midi.GenDefault (genDefaultI)
import Test.Daytripper (Expect, MonadExpect, RT, mkFileRT, mkPropRT, mkUnitRT, testRT)
import Test.Tasty (TestTree, defaultMain, testGroup)
expectStaticOk
:: (MonadExpect m, Binary a, Eq a, Show a, StaticByteSized a) => Expect m a ByteString (Either GetError a)
expectStaticOk = expectStatic expectCodecOk
genCases :: [RT]
genCases =
[ mkPropRT "MidiWord7" expectStaticOk (genDefaultI @MidiWord7)
, mkPropRT "MidiInt7" expectStaticOk (genDefaultI @MidiInt7)
, mkPropRT "MidiWord14" expectStaticOk (genDefaultI @MidiWord14)
, mkPropRT "MidiInt14" expectStaticOk (genDefaultI @MidiInt14)
, mkPropRT "VarWord" expectCodecOk (genDefaultI @VarWord)
, mkPropRT "Channel" expectStaticOk (genDefaultI @MM.Channel)
, mkPropRT "Note" expectStaticOk (genDefaultI @MM.Note)
, mkPropRT "Velocity" expectStaticOk (genDefaultI @MM.Velocity)
, mkPropRT "ControlNum" expectStaticOk (genDefaultI @MM.ControlNum)
, mkPropRT "ControlVal" expectStaticOk (genDefaultI @MM.ControlVal)
, mkPropRT "Pressure" expectStaticOk (genDefaultI @MM.Pressure)
, mkPropRT "ProgramNum" expectStaticOk (genDefaultI @MM.ProgramNum)
, mkPropRT "PitchBend" expectStaticOk (genDefaultI @MM.PitchBend)
, mkPropRT "Song" expectStaticOk (genDefaultI @MM.Song)
, mkPropRT "Position" expectStaticOk (genDefaultI @MM.Position)
, mkPropRT "ShortManf" expectStaticOk (genDefaultI @MM.ShortManf)
, mkPropRT "LongManf" expectStaticOk (genDefaultI @MM.LongManf)
, mkPropRT "Manf" expectCodecOk (genDefaultI @MM.Manf)
, mkPropRT "QuarterTime" expectStaticOk (genDefaultI @MM.QuarterTime)
, mkPropRT "UnivSysEx" expectCodecOk (genDefaultI @MM.UnivSysEx)
, mkPropRT "ManfSysEx" expectCodecOk (genDefaultI @MM.ManfSysEx)
, mkPropRT "SysExData" expectCodecOk (genDefaultI @MM.SysExData)
, mkPropRT "LiveStatus" expectStaticOk (genDefaultI @MM.LiveStatus)
, mkPropRT "RecStatus" expectStaticOk (genDefaultI @MM.RecStatus)
, mkPropRT "ShortStatus" expectStaticOk (genDefaultI @MM.ShortStatus)
, mkPropRT "MetaString" expectCodecOk (genDefaultI @MM.MetaString)
, mkPropRT "MetaData" expectCodecOk (genDefaultI @MM.MetaData)
, mkPropRT "LiveMsg" expectCodecOk (genDefaultI @MM.LiveMsg)
, mkPropRT "RecMsg" expectCodecOk (genDefaultI @MM.RecMsg)
, mkPropRT "ShortMsg" expectCodecOk (genDefaultI @MM.ShortMsg)
, mkPropRT "Track" expectCodecOk (genDefaultI @MM.Track)
, mkPropRT "MidFile" expectCodecOk (genDefaultI @MM.MidFile)
, mkPropRT "SysExDump" expectCodecOk (genDefaultI @MM.SysExDump)
, mkPropRT "RawAddrPat" expectCodecOk (genDefaultI @MOA.RawAddrPat)
, mkPropRT "PortMsg" expectCodecOk (genDefaultI @MO.PortMsg)
, mkPropRT "Msg" expectCodecOk (genDefaultI @MO.Msg)
, mkPropRT "Bundle" expectCodecOk (genDefaultI @MO.Bundle)
, mkPropRT "Packet" expectCodecOk (genDefaultI @MO.Packet)
]
testGenCases :: TestTree
testGenCases = testGroup "Gen" (fmap testRT genCases)
findFiles :: IO [FilePath]
findFiles = do
let tdDir = "testdata"
midiDir = tdDir </> "midi"
let xtraFiles = fmap (tdDir </>) ["twinkle.mid", "parse_me.mid"]
midiFiles <- fmap (fmap (midiDir </>)) (listDirectory midiDir)
pure (xtraFiles ++ midiFiles)
decodeFileAs :: (Binary a) => Proxy a -> FilePath -> IO (Either GetError a, ByteCount)
decodeFileAs _ = decodeFileEnd
shouldFail :: FilePath -> Bool
shouldFail fn =
let xs = fmap (\p -> BSC.pack ("/test-" ++ p ++ "-")) ["illegal", "non-midi", "corrupt"]
in any (`BSC.isInfixOf` BSC.pack fn) xs
suiteFileExpect :: (Binary a, Eq a, Show a) => FilePath -> Expect IO a ByteString (Either GetError a)
suiteFileExpect fn =
if shouldFail fn
then expectCodecErr
else expectCodecOk
suiteFileRT :: FilePath -> IO RT
suiteFileRT fn =
let ext = takeExtension fn
in case ext of
".mid" -> pure (mkFileRT fn (suiteFileExpect @MM.MidFile fn) fn Nothing)
".syx" -> pure (mkFileRT fn (suiteFileExpect @MM.SysExDump fn) fn Nothing)
_ -> fail ("Unhandled file format: " ++ ext)
{- FOURMOLU_DISABLE -}
oscBytes :: [Word8]
oscBytes =
[ 0x2f, 0x6f, 0x73, 0x63, 0x69, 0x6c, 0x6c, 0x61
, 0x74, 0x6f, 0x72, 0x2f, 0x34, 0x2f, 0x66, 0x72
, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x00
, 0x2c, 0x66, 0x00, 0x00, 0x43, 0xdc, 0x00, 0x00
]
{- FOURMOLU_ENABLE -}
unitCases :: [RT]
unitCases =
[ mkUnitRT
"OSC msg"
(expectBytes oscBytes expectCodecOk)
(MO.Msg "/oscillator/4/frequency" (Seq.singleton (MO.DatumFloat 440.0)))
]
-- Increase number of examples with TASTY_FALSIFY_TESTS=1000 etc
main :: IO ()
main = do
files <- findFiles
fileCases <- traverse suiteFileRT files
let testFileCases = testGroup "File" (fmap testRT fileCases)
testUnitCases = testGroup "Unit" (fmap testRT unitCases)
defaultMain $
testGroup
"Midiot"
[ testGenCases
, testUnitCases
, testFileCases
]