{-# LANGUAGE TemplateHaskell #-}
module Main where
-- To generate the golden files use a script similiar to this one
-- > ghc -ilib -isrc -itests tests/RegressionTests.hs -e 'writeGoldFile example9 "example9" defaultOptions'
-- > ghc -ilib -isrc -itests tests/RegressionTests.hs -e 'writeGoldFile example9 "example9_native" nativeOpts'
import Test.Tasty
import Test.Tasty.Golden.Advanced
import Test.Tasty.QuickCheck
import qualified Prelude
import Feldspar
import Feldspar.Compiler
import Feldspar.Compiler.Plugin
import Feldspar.Vector
import Feldspar.Vector.Internal (scan)
import qualified Feldspar.Vector.Push as PV
import Control.Monad
import Control.Monad.Error (liftIO)
import Data.Monoid ((<>))
import qualified Data.ByteString.Lazy as LB
import System.Process
import Text.Printf
example9 :: Data Int32 -> Data Int32
example9 a = condition (a<5) (3*(a+20)) (30*(a+20))
-- Compile and load example9 as c_example9 (using plugins)
loadFun ['example9]
topLevelConsts :: Data Index -> Data Index -> Data Index
topLevelConsts a b = condition (a<5) (d ! (b+5)) (c ! (b+5))
where
c = value [1,2,3,4,5] :: Data [Index]
d = value [2,3,4,5,6] :: Data [Index]
pairParam :: (Data Index, Data Index) -> Data Index
pairParam (x, _) = x
pairParam2 :: (Data Int16, Data Int16) ->
((Data Int16, Data Int16), (Data Int16, Data Int16))
pairParam2 c = (c, c)
-- One test starting.
metrics :: Vector1 IntN -> Vector1 IntN
-> Vector (Vector (Data Index, Data Index)) -> Vector (Vector1 IntN)
metrics s _ = scan (columnMetrics s) initialMetrics
initialMetrics :: Vector1 IntN
initialMetrics = replicate 8 (-32678)
columnMetrics :: Vector1 IntN -> Vector1 IntN -> Vector (Data Index, Data Index)
-> Vector1 IntN
columnMetrics s prev zf = zipWith (metricFast prev) zf s
metricFast :: Vector1 IntN -> (Data Index, Data Index) -> Data IntN -> Data IntN
metricFast prev (z, _) _ = prev ! z
-- End one test.
copyPush :: Vector1 Index -> PV.PushVector1 Index
copyPush v = let pv = PV.toPush v in pv PV.++ pv
scanlPush :: PV.PushVector1 WordN -> Vector1 WordN -> PV.PushVector (PV.PushVector1 WordN)
scanlPush = PV.scanl const
concatV :: Vector (Vector1 IntN) -> Vector1 IntN
concatV = fold (++) Empty
complexWhileCond :: Data Int32 -> (Data Int32, Data Int32)
complexWhileCond y = whileLoop (0,y) (\(a,b) -> ((\a b -> a * a /= b * b) a (b-a))) (\(a,b) -> (a+1,b))
-- One test starting
divConq3 :: Vector1 IntN -> Vector1 IntN
divConq3 xs = concatV $ pmap (map (+1)) (segment 1024 xs)
pmap :: (Syntax a, Syntax b) => (a -> b) -> Vector a -> Vector b
pmap f = map await . force . map (future . f)
segment :: Syntax a => Data Length -> Vector a -> Vector (Vector a)
segment l xs = indexed clen (\ix -> take l $ drop (ix*l) xs)
where clen = length xs `div` l
-- End one test.
-- | We rewrite `return x >>= \_ -> return y` into `return x >> return y`
-- This test ensures that we can still `return x` in the first action.
bindToThen :: Data Index -> Data Index
bindToThen y = runMutable $ do
ref <- newRef y
_ <- getRef ref
getRef ref
switcher :: Data Word8 -> Data Bool -> Data Word8
switcher i = switch (value 0) [(true,i), (false,2)]
ivartest :: Data Index -> Data Index
ivartest a = share (future (a+1)) $ \a' -> await a' * 2
ivartest2 :: (Data Index, Data Index) -> (Data Index, Data Index)
ivartest2 a = share (future a) $ \a' -> await a'
tests :: TestTree
tests = testGroup "RegressionTests"
[ testProperty "example9 (plugin)" $ eval example9 ==== c_example9
, mkGoldTest example9 "example9" defaultOptions
, mkGoldTest pairParam "pairParam" defaultOptions
, mkGoldTest pairParam2 "pairParam2" defaultOptions
, mkGoldTest concatV "concatV" defaultOptions
, mkGoldTest complexWhileCond "complexWhileCond" defaultOptions
, mkGoldTest topLevelConsts "topLevelConsts" defaultOptions
, mkGoldTest topLevelConsts "topLevelConsts_native" nativeOpts
, mkGoldTest topLevelConsts "topLevelConsts_sics" sicsOpts
, mkGoldTest metrics "metrics" defaultOptions
, mkGoldTest scanlPush "scanlPush" defaultOptions
, mkGoldTest divConq3 "divConq3" defaultOptions
, mkGoldTest ivartest "ivartest" defaultOptions
, mkGoldTest ivartest2 "ivartest2" defaultOptions
, mkBuildTest pairParam "pairParam" defaultOptions
, mkBuildTest concatV "concatV" defaultOptions
, mkBuildTest topLevelConsts "topLevelConsts" defaultOptions
, mkBuildTest topLevelConsts "topLevelConsts_native" nativeOpts
, mkBuildTest topLevelConsts "topLevelConsts_sics" sicsOpts
, mkBuildTest metrics "metrics" defaultOptions
, mkBuildTest copyPush "copyPush" defaultOptions
, mkBuildTest scanlPush "scanlPush" defaultOptions
, mkBuildTest divConq3 "divConq3" defaultOptions
, testProperty "bindToThen" (\y -> eval bindToThen y Prelude.== y)
, mkGoldTest switcher "switcher" defaultOptions
, mkBuildTest ivartest "ivartest" defaultOptions
, mkBuildTest ivartest2 "ivartest2" defaultOptions
]
main :: IO ()
main = defaultMain tests
-- Helper functions
testDir, goldDir :: Prelude.FilePath
testDir = "tests/"
goldDir = "tests/gold/"
nativeOpts :: Options
nativeOpts = defaultOptions{rules=nativeArrayRules}
sicsOpts :: Options
sicsOpts = defaultOptions{frontendOpts=defaultFeldOpts{targets= [SICS]}}
writeGoldFile :: Syntax a => a -> Prelude.FilePath -> Options -> IO ()
writeGoldFile fun n = compile fun (goldDir <> n) n
mkGoldTest fun n opts = do
let ref = goldDir <> n
new = testDir <> n
act = compile fun new n opts
cmp = simpleCmp $ printf "Files '%s' and '%s' differ" ref new
upd = LB.writeFile ref
goldenTest n (vgReadFiles ref) (liftIO act >> vgReadFiles new) cmp upd
simpleCmp :: Prelude.Eq a => String -> a -> a -> IO (Maybe String)
simpleCmp e x y =
return $ if x Prelude.== y then Nothing else Just e
vgReadFiles :: String -> ValueGetter r LB.ByteString
vgReadFiles base = liftM LB.concat $ mapM (vgReadFile . (base<>)) [".h",".c"]
mkBuildTest fun n opts = do
let new = testDir <> n <> "_build_test"
cfile = new <> ".c"
act = do compile fun new n opts
(_,so,se) <- readProcessWithExitCode "ghc" [cfile, "-c", "-optc -Ilib/Feldspar/C", "-optc -std=c99", "-Wall"] ""
return $ so <> se
cmp _ _ = return Nothing
upd _ = return ()
goldenTest n (return "") (liftIO act >> return "") cmp upd