1{-# LANGUAGE CPP, DeriveGeneric, OverloadedStrings, ScopedTypeVariables #-}
3module UnitTests (ioTests, tests) where
5import Control.Monad (forM)
6import Data.Aeson (decode, eitherDecode, encode, genericToJSON, genericToEncoding)
7import Data.Aeson.Encode (encodeToTextBuilder)
8import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions)
9import Data.Char (toUpper)
10import Data.Time (UTCTime)
11import Data.Time.Format (parseTime)
12import GHC.Generics (Generic)
13import Test.Framework (Test, testGroup)
14import Test.Framework.Providers.HUnit (testCase)
15import Test.HUnit (Assertion, assertFailure, assertEqual)
16import qualified Data.ByteString.Lazy.Char8 as L
17import qualified Data.Text.Lazy.Builder as TLB
18import qualified Data.Text.Lazy.Encoding as TLE
19import qualified Data.Text.Lazy as LT
20import qualified Data.Text.Lazy.Encoding as LT
22#if MIN_VERSION_time(1,5,0)
23import Data.Time.Format (defaultTimeLocale)
25import System.Locale (defaultTimeLocale)
29tests = testGroup "unit" [
30 testGroup "camelCase" [
31 testCase "camelTo" $ roundTripCamel "aName"
32 , testCase "camelTo" $ roundTripCamel "another"
33 , testCase "camelTo" $ roundTripCamel "someOtherName"
34 , testCase "camelTo" $
35 assertEqual "" "camel_apicase" (camelTo '_' "CamelAPICase")
36 , testCase "camelTo2" $ roundTripCamel2 "aName"
37 , testCase "camelTo2" $ roundTripCamel2 "another"
38 , testCase "camelTo2" $ roundTripCamel2 "someOtherName"
39 , testCase "camelTo2" $
40 assertEqual "" "camel_api_case" (camelTo2 '_' "CamelAPICase")
42 , testGroup "encoding" [
43 testCase "goodProducer" $ goodProducer
45 , testGroup "utctime" [
46 testCase "good" $ utcTimeGood
47 , testCase "bad" $ utcTimeBad
51roundTripCamel :: String -> Assertion
52roundTripCamel name = assertEqual "" name (camelFrom '_' $ camelTo '_' name)
54roundTripCamel2 :: String -> Assertion
55roundTripCamel2 name = assertEqual "" name (camelFrom '_' $ camelTo2 '_' name)
57camelFrom :: Char -> String -> String
58camelFrom c s = let (p:ps) = split c s
59 in concat $ p : map capitalize ps
61 split c s = map L.unpack $ L.split c $ L.pack s
62 capitalize t = toUpper (head t) : tail t
65 wibbleString :: String
67 } deriving (Generic, Show)
69instance ToJSON Wibble where
70 toJSON = genericToJSON defaultOptions
71 toEncoding = genericToEncoding defaultOptions
73-- Test that if we put a bomb in a data structure, but only demand
74-- part of it via lazy encoding, we do not unexpectedly fail.
75goodProducer :: Assertion
76goodProducer = assertEqual "partial encoding should not explode on undefined"
77 '{' (L.head (encode wibble))
80 wibbleString = replicate 4030 'a'
81 , wibbleInt = undefined
84-- Test decoding various UTC time formats
86-- Note: the incomplete pattern matches for UTCTimes are completely
87-- intentional. The test expects these parses to succeed. If the
88-- pattern matches fails, there's a bug in either the test or in aeson
89-- and needs to be investigated.
90utcTimeGood :: Assertion
92 let ts1 = "2015-01-01T12:13:00.00Z" :: LT.Text
93 let ts2 = "2015-01-01T12:13:00Z" :: LT.Text
94 -- 'T' between date and time is not required, can be space
95 let ts3 = "2015-01-03 12:13:00.00Z" :: LT.Text
96 let ts4 = "2015-01-03 12:13:00.125Z" :: LT.Text
97 let (Just (t1 :: UTCTime)) = parseWithAeson ts1
98 let (Just (t2 :: UTCTime)) = parseWithAeson ts2
99 let (Just (t3 :: UTCTime)) = parseWithAeson ts3
100 let (Just (t4 :: UTCTime)) = parseWithAeson ts4
101 assertEqual "utctime" (parseWithRead "%FT%T%QZ" ts1) t1
102 assertEqual "utctime" (parseWithRead "%FT%T%QZ" ts2) t2
103 assertEqual "utctime" (parseWithRead "%F %T%QZ" ts3) t3
104 assertEqual "utctime" (parseWithRead "%F %T%QZ" ts4) t4
105 -- Time zones. Both +HHMM and +HH:MM are allowed for timezone
106 -- offset, and MM may be omitted.
107 let ts5 = "2015-01-01T12:30:00.00+00" :: LT.Text
108 let ts6 = "2015-01-01T12:30:00.00+01:15" :: LT.Text
109 let ts7 = "2015-01-01T12:30:00.00-02" :: LT.Text
110 let ts8 = "2015-01-01T22:00:00.00-03" :: LT.Text
111 let ts9 = "2015-01-01T22:00:00.00-04:30" :: LT.Text
112 let (Just (t5 :: UTCTime)) = parseWithAeson ts5
113 let (Just (t6 :: UTCTime)) = parseWithAeson ts6
114 let (Just (t7 :: UTCTime)) = parseWithAeson ts7
115 let (Just (t8 :: UTCTime)) = parseWithAeson ts8
116 let (Just (t9 :: UTCTime)) = parseWithAeson ts9
117 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T12:30:00.00Z") t5
118 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T11:15:00.00Z") t6
119 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t7
120 -- ts8 wraps around to the next day in UTC
121 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-02T01:00:00Z") t8
122 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-02T02:30:00Z") t9
124 parseWithRead :: String -> LT.Text -> UTCTime
126 case parseTime defaultTimeLocale f . LT.unpack $ s of
127 Nothing -> error "parseTime input malformed"
129 parseWithAeson :: LT.Text -> Maybe UTCTime
130 parseWithAeson s = decode . LT.encodeUtf8 $ (LT.concat ["\"", s, "\""])
132-- Test that a few non-timezone qualified timestamp formats get
133-- rejected if decoding to UTCTime.
134utcTimeBad :: Assertion
136 verifyFailParse "2000-01-01T12:13:00" -- missing Zulu time not allowed (some TZ required)
137 verifyFailParse "2000-01-01 12:13:00" -- missing Zulu time not allowed (some TZ required)
138 verifyFailParse "2000-01-01" -- date only not OK
139 verifyFailParse "2000-01-01Z" -- date only not OK
140 verifyFailParse "2015-01-01T12:30:00.00+00Z" -- no Zulu if offset given
141 verifyFailParse "2015-01-01T12:30:00.00+00:00Z" -- no Zulu if offset given
142 verifyFailParse "2015-01-03 12:13:00.Z" -- decimal at the end but no digits
144 verifyFailParse (s :: LT.Text) =
145 let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ (LT.concat ["\"", s, "\""]) in
146 assertEqual "verify failure" Nothing dec
149------------------------------------------------------------------------------
150-- Comparison between bytestring and text encoders
151------------------------------------------------------------------------------
155 enc <- encoderComparisonTests
158encoderComparisonTests :: IO Test
159encoderComparisonTests = do
160 encoderTests <- forM testFiles $ \file0 -> do
161 let file = "benchmarks/json-data/" ++ file0
162 return $ testCase file $ do
163 inp <- L.readFile file
164 case eitherDecode inp of
165 Left err -> assertFailure $ "Decoding failure: " ++ err
166 Right val -> assertEqual "" (encode val) (encodeViaText val)
167 return $ testGroup "encoders" encoderTests
169 encodeViaText :: Value -> L.ByteString
171 TLE.encodeUtf8 . TLB.toLazyText . encodeToTextBuilder . toJSON
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list