Infrastructure for generating test data.

This commit is contained in:
2019-08-17 21:37:17 -07:00
parent 4ae957aac6
commit 84f6925f60
5 changed files with 77 additions and 16 deletions

View File

@@ -18,13 +18,14 @@ extra-source-files: CHANGELOG.md
executable generation executable generation
main-is: Main.hs main-is: Main.hs
other-modules: Base, BinaryOps, Compare, Conversions, CryptoNum, File, Gen other-modules: Base, BinaryOps, Compare, Conversions, CryptoNum, File, Gen, Testing
-- other-extensions: -- other-extensions:
build-depends: base ^>=4.12.0.0, build-depends: base ^>=4.12.0.0,
containers, containers,
directory, directory,
filepath, filepath,
mtl mtl,
random
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall ghc-options: -Wall

View File

@@ -3,16 +3,20 @@ module BinaryOps(
) )
where where
import Control.Monad(forM_) import Control.Monad(forM_,replicateM_)
import Data.Bits((.&.), (.|.), shiftL, xor)
import File import File
import Gen import Gen
binaryTestCount :: Int
binaryTestCount = 3000
binaryOps :: File binaryOps :: File
binaryOps = File { binaryOps = File {
predicate = \ _ _ -> True, predicate = \ _ _ -> True,
outputName = "binary", outputName = "binary",
generator = declareBinaryOperators, generator = declareBinaryOperators,
testGenerator = Nothing testGenerator = Just testVectors
} }
declareBinaryOperators :: Word -> Gen () declareBinaryOperators :: Word -> Gen ()
@@ -52,7 +56,7 @@ declareBinaryOperators bitsize =
out ("output.value[" ++ show i ++ "] = !self.value[" ++ show i ++ "];") out ("output.value[" ++ show i ++ "] = !self.value[" ++ show i ++ "];")
out "output" out "output"
blank blank
addBinaryLaws name addBinaryLaws name entries
generateBinOps :: String -> String -> String -> String -> Word -> Gen () generateBinOps :: String -> String -> String -> String -> Word -> Gen ()
generateBinOps trait name fun op entries = generateBinOps trait name fun op entries =
@@ -99,8 +103,8 @@ generateBinOpsFromAssigns trait name fun op =
out ("output " ++ op ++ " rhs;") out ("output " ++ op ++ " rhs;")
out "output" out "output"
addBinaryLaws :: String -> Gen () addBinaryLaws :: String -> Word -> Gen ()
addBinaryLaws name = addBinaryLaws name entries =
do let args3 = "(a: " ++ name ++ ", b: " ++ name ++ ", c: " ++ name ++ ")" do let args3 = "(a: " ++ name ++ ", b: " ++ name ++ ", c: " ++ name ++ ")"
args2 = "(a: " ++ name ++ ", b: " ++ name ++ ")" args2 = "(a: " ++ name ++ ", b: " ++ name ++ ")"
out "#[cfg(test)]" out "#[cfg(test)]"
@@ -143,4 +147,25 @@ addBinaryLaws name =
out ("(&a & &ones) == a") out ("(&a & &ones) == a")
blank blank
wrapIndent ("fn or_ident(a: " ++ name ++ ") -> bool") $ wrapIndent ("fn or_ident(a: " ++ name ++ ") -> bool") $
out ("(&a | " ++ name ++ "::zero()) == a") out ("(&a | " ++ name ++ "::zero()) == a")
wrapIndent ("fn neg_as_xor(a: " ++ name ++ ") -> bool") $
do out ("let ones = " ++ name ++ "{ value: [0xFFFFFFFFFFFFFFFFu64; "
++ show entries ++ "] };")
out ("!&a == (&ones ^ &a)")
testVectors :: Word -> Gen ()
testVectors bitsize = replicateM_ binaryTestCount $
do a <- newNum False bitsize
b <- newNum False bitsize
let o = a .|. b
c = a .&. b
n = a `xor` ((1 `shiftL` fromIntegral bitsize) - 1)
x = a `xor` b
emitTestVariable 'a' a
emitTestVariable 'b' b
emitTestVariable 'c' c
emitTestVariable 'o' o
emitTestVariable 'n' n
emitTestVariable 'x' x

View File

@@ -8,6 +8,7 @@ module File(
import Control.Monad(forM_) import Control.Monad(forM_)
import Data.Char(toUpper) import Data.Char(toUpper)
import Data.List(isPrefixOf)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Gen(Gen,blank,out) import Gen(Gen,blank,out)
import System.FilePath(takeBaseName,takeDirectory,takeFileName,(</>)) import System.FilePath(takeBaseName,takeDirectory,takeFileName,(</>))
@@ -41,9 +42,12 @@ addModuleTasks :: FilePath -> [Task] -> [Task]
addModuleTasks base baseTasks = unsignedTask : (baseTasks ++ moduleTasks) addModuleTasks base baseTasks = unsignedTask : (baseTasks ++ moduleTasks)
where where
moduleMap = foldr addModuleInfo Map.empty baseTasks moduleMap = foldr addModuleInfo Map.empty baseTasks
addModuleInfo task = addModuleInfo task map
Map.insertWith (++) (takeDirectory (outputFile task)) | base `isPrefixOf` outputFile task =
[takeBaseName (outputFile task)] Map.insertWith (++) (takeDirectory (outputFile task))
[takeBaseName (outputFile task)]
map
| otherwise = map
moduleTasks = Map.foldrWithKey generateModuleTask [] moduleMap moduleTasks = Map.foldrWithKey generateModuleTask [] moduleMap
generateModuleTask directory mods acc = acc ++ [Task { generateModuleTask directory mods acc = acc ++ [Task {
outputFile = directory </> "mod.rs", outputFile = directory </> "mod.rs",

View File

@@ -10,14 +10,19 @@ module Gen(
implFor, implFor,
implFor', implFor',
implFor'', implFor'',
newNum,
TestVariable(..),
) )
where where
import Control.Monad.RWS.Strict(RWS,evalRWS) import Control.Monad.RWS.Strict(RWS,evalRWS)
import Control.Monad.State.Class(MonadState,get,put) import Control.Monad.State.Class(MonadState,get,put)
import Control.Monad.Writer.Class(MonadWriter,tell) import Control.Monad.Writer.Class(MonadWriter,tell)
import Data.Bits(shiftL)
import Data.List(replicate) import Data.List(replicate)
import Data.Word(Word) import Data.Word(Word)
import Numeric(showHex)
import System.Random(StdGen, newStdGen, random, randomR)
newtype Gen a = Gen { unGen :: RWS () String GenState a} newtype Gen a = Gen { unGen :: RWS () String GenState a}
deriving (Applicative, Functor, Monad, MonadState GenState, MonadWriter String) deriving (Applicative, Functor, Monad, MonadState GenState, MonadWriter String)
@@ -27,15 +32,19 @@ tabAmount = 4
data GenState = GenState { data GenState = GenState {
indentAmount :: Word, indentAmount :: Word,
gensymIndex :: Word gensymIndex :: Word,
rng :: StdGen
} }
initGenState :: GenState initGenState :: IO GenState
initGenState = GenState { indentAmount = 0, gensymIndex = 0 } initGenState =
do rng0 <- newStdGen
return GenState { indentAmount = 0, gensymIndex = 0, rng = rng0 }
runGen :: FilePath -> Gen a -> IO a runGen :: FilePath -> Gen a -> IO a
runGen path action = runGen path action =
do let (res, contents) = evalRWS (unGen action) () initGenState do state0 <- initGenState
let (res, contents) = evalRWS (unGen action) () state0
writeFile path contents writeFile path contents
return res return res
@@ -85,4 +94,22 @@ implFor' trait name middle =
implFor'' :: String -> String -> Gen a -> Gen a implFor'' :: String -> String -> Gen a -> Gen a
implFor'' trait name middle = implFor'' trait name middle =
wrapIndent ("impl<'a,'b> " ++ trait ++ " for " ++ name) middle wrapIndent ("impl<'a,'b> " ++ trait ++ " for " ++ name) middle
newNum :: Bool -> Word -> Gen Integer
newNum signed bits =
do gs <- get
let rng0 = rng gs
let high = (1 `shiftL` fromIntegral bits) - 1
let (v, rng1) = randomR (0, high) rng0
let (sign, rng2) = random rng1
let v' = if signed && sign then -v else v
put gs{ rng = rng2 }
return v'
class TestVariable a where
emitTestVariable :: Char -> a -> Gen ()
instance TestVariable Integer where
emitTestVariable c v =
out ([c] ++ ": " ++ showHex v "")

View File

@@ -0,0 +1,4 @@
module Testing(
)
where