packages feed

urlencoded-0.1: test/TestDriver.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
module Main where

import Control.Monad ( liftM, replicateM, liftM2 )

import Data.Monoid ( mconcat )
import Data.Maybe ( isJust )
import Data.URLEncoded as U
import Data.List ( intercalate, intersperse )

import Network.URI
    ( isAllowedInURI, URI(..), URIAuth(..), escapeURIString, isUnreserved )

import Control.Applicative ( (<$>), Applicative(..) )

import Test.QuickCheck

instance Arbitrary URLEncoded where
    arbitrary = do
      numPairs <- sized $ \n -> choose (0, n)
      importList `liftM` replicateM numPairs (two arbitraryQueryArg)
    coarbitrary = undefined

instance Applicative Gen where
    (<*>) = liftM2 ($)
    pure = return

arbitraryQueryArg :: Gen String
arbitraryQueryArg = do
  qLen <- sized $ \n -> choose (0, n)
  replicateM qLen $ elements ['\x00'..'\xff']

httpUri :: Gen URI
httpUri = URI <$> proto <*> auth <*> path <*> query <*> frag
    where proto = (++ ":") <$> elements ["http", "https"]
          auth = sized $ \n -> if n == 0
                               then return Nothing
                               else Just <$> (URIAuth "" <$> host <*> port)
          lstart = elements ['a'..'z']
          lmid = elements $ ['a'..'z'] ++ ['0'..'9'] ++ "-"
          lend = elements $ ['a'..'z'] ++ ['0'..'9']
          hlabel = (:)
                   <$> lstart
                   <*> sized (\n -> do
                                m <- choose (0, n)
                                if m == 0
                                  then return []
                                  else (++) <$> (replicateM (m - 1) lmid)
                                           <*> (return <$> lend))
          host = intercalate "." <$> (flip replicateM hlabel =<< choose (1, 6))
          port = maybeEmpty (((':':) . show) <$> choose (1, 65535::Int))
          frag = maybeEmpty (('#':) <$> urlSafe)
          urlSafe = escapeURIString isUnreserved <$> arbitraryQueryArg
          query = maybeEmpty
                  ((('?':) . export) <$> (arbitrary :: Gen URLEncoded))
          path = sized $ \n -> do
                   m <- choose (0, n)
                   if m == 0
                     then return "/"
                     else concat <$> (replicateM m (('/':) <$> urlSafe))
          maybeEmpty g = sized $ \n -> do m <- choose (0, n)
                                          if m == 0 then return "" else g

pairLists :: Gen [(String, String)]
pairLists = sized $ \n -> do
              m <- choose (0, n)
              replicateM m $ two arbitraryQueryArg

multiLists :: String -> Gen (Int, URLEncoded)
multiLists k = sized $ \n -> do
                 m <- choose (2, 2 + n)
                 let getP = (k %=) <$> arbitraryQueryArg
                 us <- sequence $ intersperse getP $ replicate m arbitrary
                 return (m, mconcat us)

badKey :: URLEncoded -> String
badKey u = 'x':concat (keys u)

main :: IO ()
main = mapM_ quickCheck
       [ property $ \u ->
             importString (export u) == (Right u :: Either String URLEncoded)

       , property $ \u ->
           let s = export u
               expected = Right s :: Either String String
           in (export `liftM` importString s) == expected

       , property $ \u1 u2 ->
           not (U.null u1 || U.null u2) ==>
                   (export u1 ++ "&" ++ export u2) == export (u1 %& u2)

       , property $ all isAllowedInURI . export

       , property $ \u1 u2 -> (u1 %? u2) == (u1 %& u2)

       , property $ \u ->
           forAll httpUri $ \uri -> (show (uri %? u)) == (show uri %? u)

       , forAll (two arbitraryQueryArg) $ \(a, b) -> pairs (a %= b) == [(a, b)]

       , forAll pairLists $ \l -> map fst l == keys (importList l)

       , property $ \u -> urlEncode u == u

       , property $ \m ->
           urlEncode (m :: Maybe URLEncoded) == maybe empty urlEncode m

       , property $ \m ->
           "x" %=? m == maybe empty ("x" %=) (m :: Maybe Int)

       , property $ \u -> show u == export u

       , property $ \u -> all (\k -> isJust (u %! k)) $ keys u

       , property $ \u -> (u %! badKey u) == Nothing

       , property $ \u -> lookupDefault "missing" (badKey u) u == "missing"

       , forAll (multiLists "repeated") $ \(_, u) -> isJust (u %! "repeated")

       , forAll (multiLists "repeated") $ \(_, u) ->
           length (lookupAll "repeated" u) > 0

       , property $ \u -> case lookup1 (badKey u) u :: Either String String of
                            Left _ -> True
                            Right _ -> False
       ]