mysql-simple-quasi-1.0.0.0: Database/MySQL/Simple/Quasi/Test.hs
{-# OPTIONS_GHC -Wall -fno-warn-missing-signatures #-}
--module Database.MySQL.Simple.QQ.Test where
import Control.Applicative ((<$>))
import System.Exit (ExitCode(..), exitWith)
import Data.Generics (everywhere, mkT)
import Data.Generics.Text (gshow)
import Data.List (isPrefixOf)
import Database.MySQL.Simple.Quasi
import Language.Haskell.TH (runQ)
import Language.Haskell.TH.Ppr (pprint)
import Language.Haskell.TH.Quote (quoteExp)
import qualified Language.Haskell.TH.Syntax as TH
import Language.Haskell.Meta.Parse (parseExp)
qual :: [(String, String)] -> String -> String
qual prefixTgts = go prefixTgts
where
go _ [] = []
go ((prefix, tgt):more) src
| tgt `isPrefixOf` src = prefix ++ tgt ++ go prefixTgts (drop (length tgt) src)
| otherwise = go more src
go [] (c:cs) = c : go prefixTgts cs
collapseForall :: TH.Exp -> TH.Exp
collapseForall (TH.SigE e t) = TH.SigE e $ collapseForallT t
where
collapseForallT (TH.ForallT ts [] (TH.ForallT [] ctxt inner)) = TH.ForallT ts ctxt inner
collapseForallT x = x
collapseForall e = e
-- | Code from qquery uses NameG but haskell-src-meta uses NameQ.
-- This hack turns all NameG into NameQ ready for comparison:
canonName :: TH.Name -> TH.Name
canonName = TH.mkName . show
unqualName :: TH.Name -> TH.Name
unqualName = TH.mkName . TH.nameBase
asCode :: TH.Exp -> String
asCode = pprint . everywhere (mkT unqualName)
dupeQueries :: String -> String
dupeQueries ('$':'\"':rest)
= let (queryStr, '\"':more) = span (/= '\"') rest
in "(\"" ++ queryStr ++ "\", Data.String.fromString \"" ++ queryStr ++ "\")" ++ dupeQueries more
dupeQueries (c:cs) = c : dupeQueries cs
dupeQueries [] = []
(==>) :: String -> String -> IO Bool
(==>) src expS = do
act <- everywhere (mkT canonName) <$> runQ (quoteExp qquery src)
let expS' = qual [("Database.MySQL.Simple.QQ.", "QQuery") -- Handles QQuery_ too
,("Database.MySQL.Simple.QueryResults.", "QueryResults")
,("Database.MySQL.Simple.Param.", "Param")
,("Database.MySQL.Simple.Types.", "fromOnly") -- Try fromOnly before Only
,("Database.MySQL.Simple.Types.", "Only")
] . dupeQueries $ expS
case collapseForall <$> parseExp expS' of
Left e -> do putStrLn $ "Problem parsing expected output: " ++ e
return False
Right x | x == act -> return True
| otherwise -> do mapM_ putStrLn
[ "Mismatch. Expected:", " " ++ asCode x, " " ++ gshow x
, "Actual:", " " ++ asCode act, " " ++ gshow act]
return False
simple = "select * from users" ==> "QQuery_ GHC.Base.id $\"select * from users\" :: forall r. QueryResults r => QQuery_ r"
ret1 = "select id{Int} from users" ==> "QQuery_ fromOnly $\"select id from users\" :: QQuery_ Int"
ret2 = "select id{Int}, name{String} from users" ==> "QQuery_ GHC.Base.id $\"select id, name from users\" :: QQuery_ (Int, String)"
ret4 = "select id{Int}, f.*{String, Bool}, name{Double} from users, f" ==>
"QQuery_ GHC.Base.id $\"select id, f.*, name from users, f\" :: QQuery_ (Int, String, Bool, Double)"
subst1 = "select * from users where name = ?{String}" ==>
"QQuery Only GHC.Base.id $\"select * from users where name = ?\" :: forall r. QueryResults r => QQuery String r"
subst1b = "select * from users where name = ?" ==>
"QQuery Only GHC.Base.id $\"select * from users where name = ?\" :: forall q r. (Param q, QueryResults r) => QQuery q r"
subst3 = "select id{Maybe Int} from users where name = ?{Int} and name2 = ?{Bool} and name3 = ?{Maybe Double}" ==>
"QQuery GHC.Base.id fromOnly $\"select id from users where name = ? and name2 = ? and name3 = ?\" :: QQuery (Int, Bool, Maybe Double) (Maybe Int)"
subst3b = "select * from users where name = ? and name2 = ? and name3 = ?" ==>
"QQuery GHC.Base.id GHC.Base.id $\"select * from users where name = ? and name2 = ? and name3 = ?\" :: forall q q1 q2 r. (Param q, Param q1, Param q2, QueryResults r) => QQuery (q, q1, q2) r"
escaped = "select \\{curled} from whatever where text = 'hello\\?'" ==>
"QQuery_ GHC.Base.id $\"select {curled} from whatever where text = 'hello?'\" :: forall r. QueryResults r => QQuery_ r"
main :: IO ()
main = do passed <- and <$> sequence [simple, ret1, ret2, ret4, escaped, subst1, subst1b,
subst3, subst3b]
exitWith $ if passed then ExitSuccess else ExitFailure 1