packages feed

DSH-0.4: tests/Main.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}

module Main where

import qualified Database.DSH as Q
import Database.DSH (Q, QA)

-- import Database.DSH.Interpreter (fromQ)
import Database.DSH.Compiler (fromQ)

import qualified Database.HDBC as HDBC
import Database.HDBC.PostgreSQL

import Test.QuickCheck
import Test.QuickCheck.Monadic

import Data.List
import GHC.Exts

import Data.Text (Text)
import qualified Data.Text as Text

import Data.Char

instance Arbitrary Text where
  arbitrary = fmap Text.pack arbitrary

getConn :: IO Connection
getConn = connectPostgreSQL "user = 'postgres' password = 'haskell98' host = 'localhost' dbname = 'ferry'"

qc:: Testable prop => prop -> IO ()
qc = quickCheckWith stdArgs{maxSuccess = 20, maxSize = 10}

main :: IO ()
main = do
    putStrLn "Running DSH prelude tests"
    putStrLn "-------------------------"
    putStr "unit:           "
    qc prop_unit
    putStr "Bool:           "
    qc prop_bool
    putStr "Char:           "
    qc prop_char
    putStr "Text:           "
    qc prop_text
    putStr "Integer:        "
    qc prop_integer
    putStr "Double:         "
    qc prop_double

    putStrLn ""
    putStrLn "Equality & Ordering"
    putStrLn "-------------------------"
    putStr "&&:             "
    qc prop_infix_and
    putStr "||:             "
    qc prop_infix_or
    putStr "not:            "
    qc prop_not
    putStr "eq:             "
    qc prop_eq_int
    putStr "neq:            "
    qc prop_neq_int
    putStr "lt:             "
    qc prop_lt
    putStr "lte:            "
    qc prop_lte
    putStr "gt:             "
    qc prop_gt
    putStr "gte:            "
    qc prop_gte
    putStr "min_integer:    "
    qc prop_min_integer
    putStr "min_double:     "
    qc prop_min_double
    putStr "max_integer:    "
    qc prop_max_integer
    putStr "max_double:     "
    qc prop_max_double

    putStrLn ""
    putStrLn "Tuple projection functions"
    putStrLn "-------------------------"
    putStr "fst:            "
    qc prop_fst
    putStr "snd:            "
    qc prop_snd

    putStrLn ""
    putStrLn "Conditionals:"
    putStrLn "-------------------------"
    putStr "cond:           "
    qc prop_cond

    putStrLn ""
    putStrLn "Numerical operations:"
    putStrLn "-------------------------"
    putStr "add_integer:    "
    qc prop_add_integer
    putStr "add_double:     "
    qc prop_add_double
    putStr "mul_integer:    "
    qc prop_mul_integer
    putStr "mul_double:     "
    qc prop_mul_double
    putStr "div_double:     "
    qc prop_div_double
    putStr "integer_to_double: "
    qc prop_integer_to_double    
    putStr "abs_integer:    "
    qc prop_abs_integer
    putStr "abs_double:     "
    qc prop_abs_double
    putStr "signum_integer: "
    qc prop_signum_integer
    putStr "signum_double:  "
    qc prop_signum_double
    putStr "negate_integer: "
    qc prop_negate_integer
    putStr "negate_double:  "
    qc prop_negate_double

    putStrLn ""
    putStrLn "Lists"
    putStrLn "-------------------------"
    putStr "head:           "
    qc prop_head
    putStr "tail:           "
    qc prop_tail
    putStr "cons:           "
    qc prop_cons
    putStr "snoc:           "
    qc prop_snoc
    putStr "take:           "
    qc prop_take
    putStr "drop:           "
    qc prop_drop
    putStr "map_id:         "
    qc prop_map_id
    putStr "filter_True:    "
    qc prop_filter_True
    putStr "the:            "
    qc prop_the
    putStr "last:           "
    qc prop_last
    putStr "init:           "
    qc prop_init
    putStr "null:           "
    qc prop_null
    putStr "length:         "
    qc prop_length
    putStr "index:          "
    qc prop_index
    putStr "reverse:        "
    qc prop_reverse
    putStr "append:         "
    qc prop_append
    putStr "groupWith_id:   "
    qc prop_groupWith_id
    putStr "sortWith_id:    "
    qc prop_sortWith_id

    putStrLn ""
    putStrLn "Special folds"
    putStrLn "-------------------------"
    putStr "and:            "
    qc prop_and
    putStr "or:             "
    qc prop_or
    putStr "any_zero:       "
    qc prop_any_zero
    putStr "all_zero:       "
    qc prop_all_zero
    putStr "sum_integer:    "
    qc prop_sum_integer
    putStr "sum_double:     "
    qc prop_sum_double
    putStr "concat:         "
    qc prop_concat
    putStr "concatMap:      "
    qc prop_concatMap
    putStr "maximum:        "
    qc prop_maximum
    putStr "minimum:        "
    qc prop_minimum

    putStrLn ""
    putStrLn "Sublists"
    putStrLn "-------------------------"
    putStr "splitAt:        "
    qc prop_splitAt
    putStr "takeWhile:      "
    qc prop_takeWhile
    putStr "dropWhile:      "
    qc prop_dropWhile
    putStr "span:           "
    qc prop_span
    putStr "break:          "
    qc prop_break

    putStrLn ""
    putStrLn "Zipping and unzipping lists"
    putStrLn "-------------------------"
    putStr "zip:            "
    qc prop_zip
    putStr "zipWith_plus:   "
    qc prop_zipWith_plus
    putStr "unzip:          "
    qc prop_unzip

    putStrLn ""
    putStrLn "Set operations"
    putStrLn "-------------------------"
    putStr "nub:            "
    qc prop_nub


runTest :: (Eq b, QA a, QA b, Show a, Show b)
        => (Q a -> Q b)
        -> (a -> b)
        -> a
        -> Property
runTest q f arg = monadicIO $ do
    c  <- run $ getConn
    db <- run $ fromQ c (q (Q.toQ arg))
    run $ HDBC.disconnect c
    let hs = f arg
    assert (db == hs)

testNotNull :: (Eq b, Q.QA a, Q.QA b, Show a, Show b)
            => (Q.Q [a] -> Q.Q b)
            -> ([a] -> b)
            -> [a]
            -> Property
testNotNull q f arg = not (null arg) ==> runTest q f arg

runTestDouble :: (QA a, Show a)
        => (Q a -> Q Double)
        -> (a -> Double)
        -> a
        -> Property
runTestDouble q f arg = monadicIO $ do
    c  <- run $ getConn
    db <- run $ fromQ c (q (Q.toQ arg))
    run $ HDBC.disconnect c
    let hs = f arg
    let eps = 1.0E-8 :: Double;    
    assert (abs(db - hs) < eps)



uncurry_Q :: (Q.QA a, Q.QA b) => (Q.Q a -> Q.Q b -> Q.Q c) -> Q.Q (a,b) -> Q.Q c
uncurry_Q q = uncurry q . Q.view

prop_unit :: () -> Property
prop_unit = runTest id id

prop_bool :: Bool -> Property
prop_bool = runTest id id

prop_integer :: Integer -> Property
prop_integer = runTest id id

prop_double :: Double -> Property
prop_double = runTestDouble id id

isValidXmlChar :: Char -> Bool
isValidXmlChar c =
     '\x0009' <= c && c <= '\x000A'
  || '\x000D' <= c && c <= '\x000D'
  || '\x0020' <= c && c <= '\xD7FF'
  || '\xE000' <= c && c <= '\xFFFD'
  || '\x10000'<= c && c <= '\x10FFFF'

prop_char :: Char -> Property
prop_char c = isPrint c ==> runTest id id c

prop_text :: Text -> Property
prop_text t = Text.all isPrint t ==> runTest id id t



--------------------------------------------------------------------------------
-- Equality & Ordering

prop_infix_and :: (Bool,Bool) -> Property
prop_infix_and = runTest (uncurry_Q (Q.&&)) (uncurry (&&))

prop_infix_or :: (Bool,Bool) -> Property
prop_infix_or = runTest (uncurry_Q (Q.||)) (uncurry (||))

prop_not :: Bool -> Property
prop_not = runTest Q.not not

prop_eq :: (Eq a, Q.QA a, Show a) => (a,a) -> Property
prop_eq = runTest (\q -> Q.fst q Q.== Q.snd q) (\(a,b) -> a == b)

prop_eq_int :: (Integer,Integer) -> Property
prop_eq_int = prop_eq

prop_neq :: (Eq a, Q.QA a, Show a) => (a,a) -> Property
prop_neq = runTest (uncurry_Q (Q./=)) (\(a,b) -> a /= b)

prop_neq_int :: (Integer,Integer) -> Property
prop_neq_int = prop_eq

prop_lt :: (Integer, Integer) -> Property
prop_lt = runTest (uncurry_Q (Q.<)) (uncurry (<))

prop_lte :: (Integer, Integer) -> Property
prop_lte = runTest (uncurry_Q (Q.<=)) (uncurry (<=))

prop_gt :: (Integer, Integer) -> Property
prop_gt = runTest (uncurry_Q (Q.>)) (uncurry (>))

prop_gte :: (Integer, Integer) -> Property
prop_gte = runTest (uncurry_Q (Q.>=)) (uncurry (>=))

prop_min_integer :: (Integer,Integer) -> Property
prop_min_integer = runTest (uncurry_Q Q.min) (uncurry min)

prop_max_integer :: (Integer,Integer) -> Property
prop_max_integer = runTest (uncurry_Q Q.max) (uncurry max)

prop_min_double :: (Double,Double) -> Property
prop_min_double = runTestDouble (uncurry_Q Q.min) (uncurry min)

prop_max_double :: (Double,Double) -> Property
prop_max_double = runTestDouble (uncurry_Q Q.max) (uncurry max)

--------------------------------------------------------------------------------
-- Lists

prop_cons :: (Integer, [Integer]) -> Property
prop_cons = runTest (uncurry_Q (Q.<|)) (uncurry (:))

prop_snoc :: ([Integer], Integer) -> Property
prop_snoc = runTest (uncurry_Q (Q.|>)) (\(a,b) -> a ++ [b])

prop_singleton :: Integer -> Property
prop_singleton = runTest Q.singleton (\x -> [x])


-- head, tail, last, init, the and index may fail:

prop_head  :: [Integer] -> Property
prop_head  = testNotNull Q.head head

prop_tail  :: [Integer] -> Property
prop_tail  = testNotNull Q.tail tail

prop_last  :: [Integer] -> Property
prop_last  = testNotNull Q.last last

prop_init  :: [Integer] -> Property
prop_init  = testNotNull Q.init init

prop_the   :: [Integer] -> Property
prop_the l =
        allEqual l
    ==> runTest Q.the the l
  where
    allEqual []     = False
    allEqual (x:xs) = all (x ==) xs

prop_index :: ([Integer], Integer)  -> Property
prop_index (l, i) =
        i > 0 && i < fromIntegral (length l)
    ==> runTest (uncurry_Q (Q.!!))
                (\(a,b) -> a !! fromIntegral b)
                (l, i)


prop_take :: (Integer, [Integer]) -> Property
prop_take = runTest (uncurry_Q Q.take) (\(n,l) -> take (fromIntegral n) l)

prop_drop :: (Integer, [Integer]) -> Property
prop_drop = runTest (uncurry_Q Q.drop) (\(n,l) -> drop (fromIntegral n) l)

-- | Map "id" over the list
prop_map_id :: [Integer] -> Property
prop_map_id = runTest (Q.map id) (map id)

prop_append :: ([Integer], [Integer]) -> Property
prop_append = runTest (uncurry_Q (Q.><)) (\(a,b) -> a ++ b)

-- | filter "const True"
prop_filter_True :: [Integer] -> Property
prop_filter_True = runTest (Q.filter (const $ Q.toQ True)) (filter $ const True)

prop_groupWith_id :: [Integer] -> Property
prop_groupWith_id = runTest (Q.groupWith id) (groupWith id)

prop_sortWith_id  :: [Integer] -> Property
prop_sortWith_id = runTest (Q.sortWith id) (sortWith id)

prop_null :: [Integer] -> Property
prop_null = runTest Q.null null

prop_length :: [Integer] -> Property
prop_length = runTest Q.length (fromIntegral . length)

prop_reverse :: [Integer] -> Property
prop_reverse = runTest Q.reverse reverse


--------------------------------------------------------------------------------
-- Special folds

prop_and :: [Bool] -> Property
prop_and = runTest Q.and and

prop_or :: [Bool] -> Property
prop_or = runTest Q.or or

prop_any_zero :: [Integer] -> Property
prop_any_zero = runTest (Q.any (Q.== 0)) (any (== 0))

prop_all_zero :: [Integer] -> Property
prop_all_zero = runTest (Q.all (Q.== 0)) (all (== 0))

prop_sum_integer :: [Integer] -> Property
prop_sum_integer = runTest Q.sum sum

prop_sum_double :: [Double] -> Property
prop_sum_double = runTestDouble Q.sum sum

prop_concat :: [[Integer]] -> Property
prop_concat = runTest Q.concat concat

prop_concatMap :: [Integer] -> Property
prop_concatMap = runTest (Q.concatMap Q.singleton) (concatMap (\a -> [a]))


prop_maximum :: [Integer] -> Property
prop_maximum = testNotNull Q.maximum maximum

prop_minimum :: [Integer] -> Property
prop_minimum = testNotNull Q.minimum minimum

--------------------------------------------------------------------------------
-- Sublists

prop_splitAt :: (Integer, [Integer]) -> Property
prop_splitAt = runTest (uncurry_Q Q.splitAt) (\(a,b) -> splitAt (fromIntegral a) b)

prop_takeWhile :: (Integer, [Integer]) -> Property
prop_takeWhile = runTest (uncurry_Q $ Q.takeWhile . (Q.==))
                         (uncurry   $   takeWhile . (==))

prop_dropWhile :: (Integer, [Integer]) -> Property
prop_dropWhile = runTest (uncurry_Q $ Q.dropWhile . (Q.==))
                         (uncurry   $   dropWhile . (==))

prop_span :: (Integer, [Integer]) -> Property
prop_span = runTest (uncurry_Q $ Q.span . (Q.==))
                    (uncurry   $   span . (==) . fromIntegral)

prop_break :: (Integer, [Integer]) -> Property
prop_break = runTest (uncurry_Q $ Q.break . (Q.==))
                     (uncurry   $   break . (==) . fromIntegral)


--------------------------------------------------------------------------------
-- Zipping and unzipping lists

prop_zip :: ([Integer], [Integer]) -> Property
prop_zip = runTest (uncurry_Q Q.zip) (uncurry zip)

prop_zipWith_plus :: ([Integer], [Integer]) -> Property
prop_zipWith_plus = runTest (uncurry_Q $ Q.zipWith (+)) (uncurry $ zipWith (+))

prop_unzip :: [(Integer, Integer)] -> Property
prop_unzip = runTest Q.unzip unzip


--------------------------------------------------------------------------------
-- Set operations

prop_nub :: [Integer] -> Property
prop_nub = runTest Q.nub nub


--------------------------------------------------------------------------------
-- Tuple projection functions

prop_fst :: (Integer, Integer) -> Property
prop_fst = runTest Q.fst fst

prop_snd :: (Integer, Integer) -> Property
prop_snd = runTest Q.snd snd


--------------------------------------------------------------------------------
-- Conditionals

prop_cond :: Bool -> Property
prop_cond = runTest (Q.cond Q.empty (Q.toQ [0 :: Integer]))
                    (\b -> if b then [] else [0])

--------------------------------------------------------------------------------
-- Numerical Operations

prop_add_integer :: (Integer,Integer) -> Property
prop_add_integer = runTest (uncurry_Q (+)) (uncurry (+))

prop_add_double :: (Double,Double) -> Property
prop_add_double = runTestDouble (uncurry_Q (+)) (uncurry (+))

prop_mul_integer :: (Integer,Integer) -> Property
prop_mul_integer = runTest (uncurry_Q (*)) (uncurry (*))

prop_mul_double :: (Double,Double) -> Property
prop_mul_double = runTestDouble (uncurry_Q (*)) (uncurry (*))

prop_div_double :: (Double,Double) -> Property
prop_div_double (x,y) =
      y /= 0
  ==> runTestDouble (uncurry_Q (/)) (uncurry (/)) (x,y)

prop_integer_to_double :: Integer -> Property
prop_integer_to_double = runTestDouble Q.integerToDouble fromInteger

prop_abs_integer :: Integer -> Property
prop_abs_integer = runTest Q.abs abs

prop_abs_double :: Double -> Property
prop_abs_double = runTestDouble Q.abs abs

prop_signum_integer :: Integer -> Property
prop_signum_integer = runTest Q.signum signum

prop_signum_double :: Double -> Property
prop_signum_double = runTestDouble Q.signum signum

prop_negate_integer :: Integer -> Property
prop_negate_integer = runTest Q.negate negate

prop_negate_double :: Double -> Property
prop_negate_double = runTestDouble Q.negate negate