{-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wall #-} module Test.BitVector (bitVectorTestGroup) where import Test.Tasty import Test.Tasty.QuickCheck import Test.Tasty.HUnit import Test.Tasty.TH import qualified Test.QuickCheck.Monadic as QM import Control.Monad import Data.Maybe #if !MIN_VERSION_base(4,11,0) import Data.Monoid #endif import ToySolver.Data.OrdRel import qualified ToySolver.BitVector as BV -- ------------------------------------------------------------------------ prop_BVSolver :: Property prop_BVSolver = QM.monadicIO $ do (vs,cs) <- QM.pick genFormula ret <- QM.run $ do solver <- BV.newSolver forM_ vs $ \v -> do v' <- BV.newVar solver (BV.varWidth v) unless (v' == BV.EVar v) undefined -- XXX return () forM_ cs $ \c -> do BV.assertAtom solver c Nothing ret <- BV.check solver if ret then do m <- BV.getModel solver return $ Just m else return Nothing case ret of Nothing -> return () Just m -> do QM.monitor (counterexample $ show m) QM.assert $ and [BV.evalAtom m c | c <- cs] case_division_by_zero_cong :: IO () case_division_by_zero_cong = do solver <- BV.newSolver v1 <- BV.newVar solver 8 v2 <- BV.newVar solver 8 let z = BV.nat2bv 8 0 BV.assertAtom solver (BV.bvudiv v1 z ./=. BV.bvudiv v2 z) Nothing ret <- BV.check solver ret @?= True BV.assertAtom solver (v1 .==. v2) Nothing ret2 <- BV.check solver ret2 @?= False -- ------------------------------------------------------------------------ -- Generators genBVExpr :: [BV.Expr] -> Int -> Int -> Gen BV.Expr genBVExpr bases = f where f :: Int -> Int -> Gen BV.Expr f w s = do let gBase | w > 0 && wmax >= w = Just $ do e <- elements [e | e <- bases, BV.width e >= w] if BV.width e == w then do return e else do l <- choose (0, BV.width e - w) -- inclusive range let u = l + w - 1 return $ BV.extract u l e | otherwise = Nothing where wmax = maximum (0 : map BV.width bases) gConst = do bs <- replicateM w arbitrary return $ BV.fromDescBits bs gConcat = do if s <= 0 then do w1 <- choose (1,w-1) lhs <- f w1 0 rhs <- f (w - w1) 0 return $ lhs <> rhs else do w1 <- choose (0,w) s1 <- choose (0,s) lhs <- f w1 s1 rhs <- f (w - w1) (s - s1) return $ lhs <> rhs gExtract = do wd <- choose (0,10) e <- f (w + wd) (s - 1) l <- choose (0, BV.width e - w) -- inclusive range let u = l + w - 1 return $ BV.extract u l e gNormalOp1 = do op <- elements [BV.bvnot, BV.bvneg] e <- f w (s - 1) return $ op e gNormalOp2 = do op <- elements $ [BV.bvand, BV.bvor, BV.bvxor, BV.bvnand, BV.bvnor, BV.bvxnor] ++ [BV.bvadd, BV.bvsub, BV.bvmul, BV.bvudiv, BV.bvurem, BV.bvshl, BV.bvlshr, BV.bvashr] ++ (if w >= 1 then [BV.bvsdiv, BV.bvsrem, BV.bvsmod] else []) s1 <- choose (0,s) lhs <- f w s1 rhs <- f w (s - s1) return $ op lhs rhs gComp = do w1 <- choose (0, wmax*2) s1 <- choose (0,s) lhs <- f w1 s1 rhs <- f w1 (s - s1) return $ BV.bvcomp lhs rhs where wmax = maximum (0 : map BV.width bases) if s <= 0 then do oneof $ maybeToList gBase ++ [gConst] ++ [gConcat | w >= 2] else do oneof $ maybeToList gBase ++ [gConst, gNormalOp1, gNormalOp2] ++ (if w > 0 then [gConcat, gExtract] else []) ++ [gComp | w == 1] genBVAtom :: [BV.Expr] -> Int -> Gen BV.Atom genBVAtom bases size = do w <- choose (0,16) s1 <- choose (0,size) e1 <- genBVExpr bases w s1 e2 <- genBVExpr bases w (size - s1) op <- elements [Lt, Le, Ge, Gt, Eql, NEq] signed <- arbitrary return $ BV.Rel (ordRel op e1 e2) signed genFormula :: Gen ([BV.Var], [BV.Atom]) genFormula = do vs <- forM [0..2] $ \v -> do w <- choose (1,16) return $ BV.Var{ BV.varWidth = w, BV.varId = v } let bases = [BV.EVar v | v <- vs] nc <- choose (0,3) cs <- replicateM nc $ do size <- choose (1,8) genBVAtom bases size return (vs,cs) -- ------------------------------------------------------------------------ -- Test harness bitVectorTestGroup :: TestTree bitVectorTestGroup = $(testGroupGenerator)