{- | Module : Test.RandomStrings Description : Generate random strings for testing A way to generate random character strings for testing. Functions allow tuning of strings, setting the probability of alphabetic and upper-case characters in the resulting strings. Hopefully this is useful for building test and benchmark cases that are more meaningful than the overly random strings generated by libraries like 'Test.QuickCheck'. Note: /Please don't use this to generate passwords!/ Examples: Generate a 10-letter word with 1 in 10 upper-case characters > word <- randomWord' randomASCII (1%10) 10 Generate a list of 500 strings from the printable ISO-8859-1 characters with random lengths beween 5 and 20 characters. > let iso_printable = randomString $ onlyPrintable randomChar8 > strings <- randomStringsLen iso_printable (5,20) 500 > > -- benchmarks ... > -} module Test.RandomStrings ( -- * Character generators randomChar , randomASCII , randomChar8 -- * Specific character generators , onlyWith , onlyPrintable , onlyAlpha , onlyAlpha' , onlyAlphaNum , onlyUpper , onlyLower , randomClass -- * String generators , randomWord , randomWord' , randomString , randomString' -- * Sets of randomly generated strings , randomStrings , randomStringsLen ) where import Data.Bool ( bool ) import Data.Char import Data.Ratio import Control.Monad import System.Random import Test.RandomStrings.Internal -- | Generate a random Char randomChar :: IO Char randomChar = getStdRandom $ random -- | Generate a random ASCII (7-bit) char in the printable range. randomASCII :: IO Char randomASCII = getStdRandom $ randomR (chr 0,chr 127) -- | Generate a random ISO-8859-1 (8-bit) char randomChar8 :: IO Char randomChar8 = getStdRandom $ randomR (chr 0,chr 255) -- | Random character passing a test onlyWith :: (Char -> Bool) -- ^ predicate, like 'isAlpha' -> IO Char -- ^ random char generator, like 'randomChar' or 'randomASCII' or 'randomChar8' -> IO Char onlyWith p gen = gen >>= \c -> if p c then return c else onlyWith p gen -- | Supply a random printable character. onlyPrintable :: IO Char -> IO Char onlyPrintable = onlyWith isPrint -- | Generate a random printable non-alphabet char. onlyNonAlpha :: IO Char -> IO Char onlyNonAlpha = onlyWith (not . isAlpha) . onlyPrintable -- | Generate a random alphabetic char. onlyAlpha :: IO Char -> IO Char onlyAlpha = onlyWith isAlpha -- | Generate a random alphabetic char with a probability of being upper-case. onlyAlpha' :: Rational -- ^ range 0 to 1; chance of being an upper -> IO Char -- ^ random char generator; 'randomChar', 'randomASCII', 'randomChar8' -> IO Char onlyAlpha' r gen = randomClass r (onlyUpper gen) (onlyLower gen) -- | Generate an alphanumeric char. onlyAlphaNum :: IO Char -> IO Char onlyAlphaNum = onlyWith isAlphaNum -- | Randomly generate one of two character types randomClass :: Rational -- ^ range 0 to 1; chance of using the first generator -> IO Char -- ^ first generator; used if the random value is @True@ -> IO Char -- ^ second generator; used if the random value is @False@ -> IO Char randomClass r t f = randomBool r >>= bool f t -- | Generate a random upper-case letter. onlyUpper :: IO Char -> IO Char onlyUpper = onlyWith isUpper -- | Generate a random lower-case letter. onlyLower :: IO Char -> IO Char onlyLower = onlyWith isLower -- | Generate a random string of alphabetic characters. randomWord :: IO Char -- ^ random char generator; 'randomChar' or 'randomASCII' or 'randomChar8' -> Int -- ^ length -> IO String randomWord gen len = replicateM len $ onlyAlpha gen randomWord' :: IO Char -- ^ random char generator; 'randomChar' or 'randomASCII' or 'randomChar8' -> Rational -- ^ range 0 to 1; fraction of upper-case letters -> Int -- ^ length -> IO String randomWord' gen r len = replicateM len $ onlyAlpha' r gen -- | Generate a random string randomString :: IO Char -- ^ random char generator; eg. 'randomChar8' or @onlyAlpha randomASCII@ -> Int -- ^ length -> IO String randomString = flip replicateM -- | Generate a random string of printable characters with a balance of -- alphabetic and upper-case characters. randomString' :: IO Char -- ^ random char generator; 'randomChar' or 'randomASCII' or 'randomChar8' -> Rational -- ^ range 0 to 1; fraction of alphabetic characters -> Rational -- ^ range 0 to 1; fraction of upper-case letters -> Int -- ^ length -> IO String randomString' gen ra ru len = replicateM len $ randomClass ra (randomClass ru (onlyUpper gen) (onlyLower gen)) (onlyNonAlpha gen) -- | Generate a list of strings of uniform length. -- -- > randomStrings (randomString (onlyAlpha randomChar8) 20) 50 -- -- will build a list of 50 alphabetical strings, each 20 characters long. -- randomStrings :: IO String -- ^ random string generator, eg. 'randomString randomAlpha 20' -> Int -- ^ list length -> IO [String] randomStrings = flip replicateM -- | Generate a list of strings of variable length. -- -- Similar to 'randomStrings', but generates strings with random length. -- Example: -- -- > randomStringsLen (randomString' randomASCII (3%4) (1%8)) (10,30) 100 -- -- Returns a list of 100 strings that are between 10 and 30 characters, -- with 3/4 of them being alphabetical and 1/8 of those being upper-case. -- randomStringsLen :: (Int -> IO String) -- ^ random string generator, eg. @randomString randomAlpha@ -> (Int,Int) -- ^ range for string length -> Int -- ^ list length -> IO [String] randomStringsLen gen range len = replicateM len $ getStdRandom (randomR range) >>= gen