packages feed

elocrypt 0.3.2 → 0.4.0

raw patch · 7 files changed

+186/−38 lines, 7 filesdep +proctestPVP ok

version bump matches the API change (PVP)

Dependencies added: proctest

API changes (from Hackage documentation)

- Data.Elocrypt: lastN :: MonadRandom m => String -> Int -> m String
+ Data.Elocrypt: lastN :: MonadRandom m => Int -> String -> m String

Files

README.md view
@@ -1,11 +1,30 @@ # Elocrypt [![Build Status](https://travis-ci.org/sgillespie/elocrypt.svg?branch=master)](https://travis-ci.org/sgillespie/elocrypt) -Elocrypt is a Haskell library that generates pronounceable, hard-to-guess passwords.. as hard as Vince Carter's knee cartilage is. Elocrypt includes a library and program.+Elocrypt generates pronounceable, easy-to-remember, hard-to-guess passwords... as hard as Vince Carter's knee cartilage is. Elocrypt includes a Haskell library and program. -## Downloading-Elocrypt sources can be found @ https://github.com/sgillespie/elocrypt+## Prerequisites+In order to build or install you will need+ * GHC (tested on 7.10 and 7.8)+ * cabal-install (tested on 1.20 and 1.22) +## Installing+Elocrypt is on [Hackage](https://hackage.haskell.org/package/elocrypt).  Installation is as easy as:+```+cabal install elocrypt+```++## Running+Running elocrypt is as simple as:+```+elocrypt [length]+```++## Obtaining the source+Elocrypt sources can be found + * https://github.com/sgillespie/elocrypt+ * https://hackage.haskell.org/package/elocrypt+ ## Building In order to build or install you will need  * GHC (tested on 7.10 and 7.8)@@ -21,20 +40,14 @@ cabal install ``` -## Running-Running elocrypt is as simple as...-```-elocrypt [length]-```- ## API Documentation-The full API documentation is generated by haddock+The full API documentation is on hackage @ https://hackage.haskell.org/package/elocrypt/docs. To build the documentation yourself, run ``` cabal haddock ```  ### API Examples-You can use elocrypt to generate words in any Haskell code, so long as you have installed elocrypt. Generate a word by using Elocrypt.Password.generate+You can use elocrypt to generate words in any Haskell code, so long as you have installed elocrypt. Generate a word by using Data.Elocrypt.newPassword ``` import Data.Elocrypt ...@@ -43,7 +56,7 @@ fooGen = newPassword 10 `liftM` getStdGen ``` -Alternatively, you can use Elocrypt.Password.mkPassword if you want to complete control of the random monad+Alternatively, you can use Data.Elocrypt.mkPassword if you want to complete control of the random monad ``` import Data.Elocrypt import Control.Monad.Random
+ cli-test/Tests.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE TemplateHaskell #-}+module Main where++import Data.Functor+import Data.List+import Data.Maybe+import Control.Monad+import Text.Printf++import Test.Proctest+import Test.Proctest.Assertions+import Test.QuickCheck+import Test.QuickCheck.Property+import Test.Tasty hiding (Timeout)+import Test.Tasty.QuickCheck (QuickCheckTests(..), testProperty)+import Test.Tasty.TH++import Test.Elocrypt.Instances++main :: IO ()+main = defaultMain (options tests)++options :: TestTree -> TestTree+options = localOption (QuickCheckTests 10)++tests :: TestTree+tests = testGroup "CLI Tests" [testGroup']++testGroup' = $(testGroupGenerator)++newtype CliArgs = CliArgs  { getArgs :: [String] }+               deriving Eq++instance Show CliArgs where+  show = concat . intersperse " " . getArgs++instance Arbitrary CliArgs where+  arbitrary = do+    len  <- arbitrary `suchThat` (>2) `suchThat` (<=40) :: Gen Int+    num  <- arbitrary `suchThat` (>2) `suchThat` (<=20) :: Gen Int+    args <- sublistOf ["-n %d" `printf` num,+                       show len]+    return (CliArgs args)+              +prop_shouldPrintPasswordsWithLength :: CliArgs -> Property+prop_shouldPrintPasswordsWithLength (CliArgs args)+  = isJust (getPosParam args) ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'++      let len    = read . fromJust . getPosParam $ args+          words' = words response++      return (all ((==) len . length) words')++prop_shouldPrintNumberPasswords :: CliArgs -> Property+prop_shouldPrintNumberPasswords (CliArgs args)+  = isJust (getArg "-n" args) ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'++      let number = read . fromJust . getArg "-n" $ args+          words' = words response+          +      return (number == length words')++prop_shouldPrintMultPasswordsPerLine :: CliArgs -> Property+prop_shouldPrintMultPasswordsPerLine (CliArgs args)+  = (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'+      return . all (>1) . tail . reverse . map length . map words . lines $ response++prop_shouldPrintDefaultMultPasswordsPerLine :: CliArgs -> Property+prop_shouldPrintDefaultMultPasswordsPerLine (CliArgs args)+  = isNothing (getArg "-n" args) ==>+    (read . fromMaybe "8" . getPosParam $ args) <= 38 ==>+    ioProperty $ do+      (in', out', err', p) <- run' "dist/build/elocrypt/elocrypt" args+      response <- readHandle out'+      return . all (>1) . tail . reverse . map length . map words . lines $ response++-- Utility functions+run' :: FilePath -> [String] -> IO (Handle, Handle, Handle, ProcessHandle)+run' exe args = do+  res@(in', out', err', p) <- run exe args+  sleep'+  assertExitedSuccess (seconds 2) p+  return res++readHandle :: Handle -> IO String+readHandle = (<$>) asUtf8Str . waitOutput (seconds 2) 5000++assertExitedSuccess :: Timeout -> ProcessHandle -> IO Bool+assertExitedSuccess t = liftM (== ExitSuccess) . assertExitedTimeout t++assertExitedFailure :: Timeout -> ProcessHandle -> IO Bool+assertExitedFailure t = liftM not . assertExitedSuccess t++sleep' :: IO ()+sleep' = sleep (seconds 0.0001)++getArg :: String -> [String] -> Maybe String+getArg prefix args = (tail . dropWhile (not . elem')) `liftM` arg+  where arg = find (isPrefixOf prefix) args+        elem' = (flip elem) [' ', '=']++getPosParam :: [String] -> Maybe String+getPosParam = find $ (/= '-') . head
elocrypt.cabal view
@@ -1,5 +1,5 @@ name:                elocrypt-version:             0.3.2+version:             0.4.0 synopsis:            Generate easy-to-remember, hard-to-guess passwords homepage:            https://www.github.com/sgillespie/elocrypt license:             OtherLicense@@ -26,7 +26,7 @@ source-repository this   type: git   location: https://github.com/sgillespie/elocrypt.git-  tag: v0.3.2+  tag: v0.4.0                    library   exposed-modules:     Data.Elocrypt,@@ -58,5 +58,21 @@                        tasty-th   default-language:    Haskell2010   hs-source-dirs:      test+  main-is:             Tests.hs+  type:                exitcode-stdio-1.0++test-suite ui-test+  build-depends:       base >= 4.7 && <4.9,+                       elocrypt,+                       proctest,+                       MonadRandom,+                       QuickCheck,+                       random,+                       tasty,+                       tasty-quickcheck,+                       tasty-th                      +  default-language:    Haskell2010+  hs-source-dirs:      cli-test,+                       test   main-is:             Tests.hs   type:                exitcode-stdio-1.0
src/cli/Main.hs view
@@ -2,6 +2,7 @@  import Control.Monad import Data.List+import Data.Maybe (fromMaybe) import System.Console.GetOpt import System.Environment import System.Exit@@ -10,7 +11,9 @@  import Data.Elocrypt -version = "elocrypt 0.3.2"+version = "elocrypt 0.4.0"+termLen = 80+termHeight = 10  main :: IO () main = do@@ -18,41 +21,42 @@   opts <- elocryptOpts args   gen  <- getStdGen -  -- TODO: Refactor me-  if (optHelp opts) then do+  when (optHelp opts) $ do     hPutStrLn stderr usage     exitSuccess-  else return ()--  -- TODO: Refactor me-  if (optVersion opts) then do+  when (optVersion opts) $ do     hPutStrLn stderr version     exitSuccess-  else return ()    putStrLn (passwords opts gen)  passwords :: RandomGen g => Options -> g -> String-passwords opts gen = concat . intersperse "\n" . map (concat . intersperse "\t") . group $ ps-  where ps = take (optNumber opts) . newPasswords (optLength opts) $ gen-        group (p:ps:pss:psss) = [p, ps, pss]:(group psss)-        group ps = [ps]+passwords opts gen = format . group' cols $ ps+  where ps = take num . newPasswords (optLength opts) $ gen+        format = concat . intersperse "\n" . map (concat . intersperse "  ")+        cols = termLen `div` (optLength opts + 2)+        num = fromMaybe (cols * termHeight) (optNumber opts) +group' :: Int -> [a] -> [[a]]+group' _ [] = []+group' i ls = g:(group' i ls')+  where (g, ls') = splitAt i ls+ data Options-  = Options { optLength  :: Int,    -- Size of the password(s)-              optNumber  :: Int,    -- Number of passwords to generate+  = Options { optLength  :: Int,        -- Size of the password(s)+              optNumber  :: Maybe Int,  -- Number of passwords to generate               optHelp    :: Bool,               optVersion :: Bool }   deriving (Show)  defaultOptions :: Options defaultOptions = Options { optLength = 8,-                           optNumber = 42,+                           optNumber = Nothing,                            optHelp = False,                            optVersion = False }  options :: [OptDescr (Options -> Options)]-options = [Option ['n'] ["number"] (ReqArg (\n o -> o {optNumber = read n}) "NUMBER") "The number of passwords to generate (default: 42)",+options = [Option ['n'] ["number"] (ReqArg (\n o -> o {optNumber = Just (read n)}) "NUMBER") "The number of passwords to generate",            Option ['h'] ["help"] (NoArg (\o -> o { optHelp = True })) "Show this help",            Option ['v'] ["version"] (NoArg (\o -> o { optVersion = True })) "Show version and exit"] 
src/lib/Data/Elocrypt.hs view
@@ -85,7 +85,8 @@ mkPassword :: MonadRandom m               => Int -- ^ password length               -> m String-mkPassword len = reverse `liftM` first2 >>= (flip lastN) (len - 2) >>= return . reverse+mkPassword len = first2 >>= reverse' >>= lastN (len - 2) >>= reverse'+  where reverse' = return . reverse  -- |Plural version of mkPassword.  Generate an infinite list of passwords using --  the MonadRandom m.  MonadRandom is exposed here for extra control.@@ -106,9 +107,11 @@  -- * Internal --- |Generate two random characters+-- |Generate two random characters. Uses 'Elocrypt.Trigraph.trigragh'+--  to generate a weighted list. first2 :: MonadRandom m => m String-first2 = sequence . take 2 . repeat . uniform $ alphabet+first2 = fromList (map toWeight frequencies)+  where toWeight (s, w) = (s, sum w)  -- |Generate a random character based on the previous two characters and --  their 'Elocrypt.Trigraph.trigraph'@@ -117,6 +120,6 @@  -- |Generate the last n characters using previous two characters --  and their 'Elocrypt.Trigraph.trigraph'-lastN :: MonadRandom m => String -> Int -> m String-lastN ls 0 = return ls-lastN ls len = next ls >>= (flip lastN) (len - 1) . (flip (:)) ls+lastN :: MonadRandom m => Int -> String -> m String+lastN 0 ls   = return ls+lastN len ls = next ls >>= lastN (len - 1) . (:ls)
test/Test/Elocrypt/Instances.hs view
@@ -13,7 +13,7 @@ instance Arbitrary AlphaChar where   arbitrary = Alpha `liftM` elements ['a'..'z'] -newtype GreaterThan2 a = GT2 { greaterThan2 :: a }+newtype GreaterThan2 a = GT2 { getGT2 :: a }                        deriving (Eq, Ord, Show)  instance (Num a, Ord a, Arbitrary a) => Arbitrary (GreaterThan2 a) where
test/Test/ElocryptTest.hs view
@@ -48,7 +48,7 @@ prop_lastNShouldSkip0Weights (Alpha c1) (Alpha c2) len gen   = len > 0 ==> lastNShouldSkip0Weights' lastN'   where f2 = [c1, c2]-        lastN' = evalRand (lastN (reverse [c1, c2]) len) gen+        lastN' = evalRand (lastN len [c2, c1]) gen  lastNShouldSkip0Weights' :: String -> Property lastNShouldSkip0Weights' (p:ps:pss:psss) = isCandidate p [pss, ps] .&&.