2018-02-23 12:51:03 +05:30
|
|
|
module Main where
|
|
|
|
|
2018-03-03 10:46:35 +05:30
|
|
|
import Control.Monad (unless, forM)
|
2018-03-02 20:34:02 +05:30
|
|
|
import Data.Char (isControl)
|
2018-03-03 10:46:35 +05:30
|
|
|
import Data.List (intercalate)
|
2018-02-23 12:51:03 +05:30
|
|
|
import System.Exit
|
|
|
|
import Test.QuickCheck
|
|
|
|
import Test.QuickCheck.Test (isSuccess)
|
|
|
|
|
|
|
|
import JSONParser
|
|
|
|
|
2018-02-21 20:04:01 +05:30
|
|
|
main :: IO ()
|
2018-02-23 12:51:03 +05:30
|
|
|
main = do
|
2018-03-03 10:46:35 +05:30
|
|
|
result <- quickCheckResult checkJSON
|
2018-02-23 12:51:03 +05:30
|
|
|
unless (isSuccess result) exitFailure
|
|
|
|
|
2018-03-03 10:46:35 +05:30
|
|
|
checkJSON :: JValue -> Gen Property
|
|
|
|
checkJSON value = do
|
|
|
|
json <- stringify value
|
|
|
|
return . counterexample (show json) . (== Just value) . parseJSON $ json
|
|
|
|
|
|
|
|
stringify :: JValue -> Gen String
|
|
|
|
stringify = pad . go
|
|
|
|
where
|
|
|
|
surround l r j = l ++ j ++ r
|
|
|
|
pad gen = surround <$> jsonWhitespaceGen <*> jsonWhitespaceGen <*> gen
|
|
|
|
commaSeparated = pad . pure . intercalate ","
|
|
|
|
|
|
|
|
go value = case value of
|
|
|
|
JArray elements ->
|
|
|
|
mapM (pad . stringify) elements >>= fmap (surround "[" "]") . commaSeparated
|
|
|
|
JObject kvs ->
|
|
|
|
mapM stringifyKV kvs >>= fmap (surround "{" "}") . commaSeparated
|
|
|
|
_ -> return $ show value
|
|
|
|
|
|
|
|
stringifyKV (k, v) = surround <$> pad (pure $ show k) <*> stringify v <*> pure ":"
|
|
|
|
|
2018-02-23 12:51:03 +05:30
|
|
|
instance Arbitrary JValue where
|
2018-03-02 20:34:02 +05:30
|
|
|
shrink = genericShrink
|
|
|
|
|
2018-02-23 12:51:03 +05:30
|
|
|
arbitrary = sized go
|
|
|
|
where
|
2018-03-03 10:46:35 +05:30
|
|
|
go n | n < 5 = frequency [(4, oneof scalarGens), (1, oneof (compositeGens n))]
|
|
|
|
go n = frequency [(1, oneof scalarGens), (4, oneof (compositeGens n))]
|
2018-03-02 20:34:02 +05:30
|
|
|
|
2018-02-23 12:51:03 +05:30
|
|
|
scalarGens = [ pure JNull
|
|
|
|
, JBool <$> arbitrary
|
2018-03-03 10:46:35 +05:30
|
|
|
, JString <$> jsonStringGen
|
|
|
|
, JNumber <$> arbitrary <*> listOf digits <*> arbitrary
|
2018-02-23 12:51:03 +05:30
|
|
|
]
|
|
|
|
|
2018-03-02 20:34:02 +05:30
|
|
|
compositeGens n = [ fmap JArray . scaledListOf . go $ n `div` 2
|
|
|
|
, fmap JObject . scaledListOf . objKV $ n `div` 2
|
|
|
|
]
|
2018-02-23 12:51:03 +05:30
|
|
|
|
2018-03-03 10:46:35 +05:30
|
|
|
objKV n = (,) <$> jsonStringGen <*> go n
|
|
|
|
|
|
|
|
scaledListOf = scale (`div` 2) . listOf
|
2018-02-23 12:51:03 +05:30
|
|
|
|
2018-03-03 10:46:35 +05:30
|
|
|
digits = choose (0, 9)
|
2018-02-23 12:51:03 +05:30
|
|
|
|
2018-03-03 10:46:35 +05:30
|
|
|
jsonStringGen :: Gen String
|
|
|
|
jsonStringGen = listOf arbitraryJSONChar
|
2018-03-02 20:34:02 +05:30
|
|
|
where
|
|
|
|
arbitraryJSONChar' =
|
|
|
|
frequency [(9, arbitraryASCIIChar), (0, choose ('\128', '\65535'))]
|
|
|
|
`suchThat` (\c -> not (c == '\"' || c == '\\' || isControl c))
|
2018-02-23 12:51:03 +05:30
|
|
|
|
2018-03-02 20:34:02 +05:30
|
|
|
arbitraryJSONChar = frequency [
|
|
|
|
(99, arbitraryJSONChar')
|
|
|
|
, (1, elements ['"' , '\\' , '\b' , '\f' , '\n' , '\r' , '\t'])
|
|
|
|
]
|
2018-03-03 10:46:35 +05:30
|
|
|
|
|
|
|
jsonWhitespaceGen :: Gen String
|
|
|
|
jsonWhitespaceGen =
|
|
|
|
scale (round . sqrt . fromIntegral)
|
|
|
|
. listOf
|
|
|
|
. elements
|
|
|
|
$ [' ' , '\n' , '\r' , '\t']
|