Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 219 lines (192 sloc) 8.591 kB
74b3f35 @tibbe Fix test bug
authored
1 {-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
87a4daf @tibbe Add tests for name-based decoding
authored
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
3
1fae7b8 @tibbe Add unit test
authored
4 module Main
5 ( main
6 ) where
7
8 import qualified Data.ByteString as B
9 import qualified Data.ByteString.Lazy as BL
da2cd94 @tibbe Clean up tests
authored
10 import qualified Data.ByteString.Lazy.Char8 as BL8
dd3dda1 @tibbe encodeByHeader: Fix bug where header was omitted
authored
11 import qualified Data.HashMap.Strict as HM
875fd47 @tibbe Add round-trip unit tests
authored
12 import Data.Int
13 import qualified Data.Text as T
14 import qualified Data.Text.Lazy as LT
15 import Data.Vector ((!))
1fae7b8 @tibbe Add unit test
authored
16 import qualified Data.Vector as V
875fd47 @tibbe Add round-trip unit tests
authored
17 import Data.Word
1fae7b8 @tibbe Add unit test
authored
18 import Test.HUnit
19 import Test.Framework as TF
20 import Test.Framework.Providers.HUnit as TF
875fd47 @tibbe Add round-trip unit tests
authored
21 import Test.QuickCheck
22 import Test.Framework.Providers.QuickCheck2 as TF
1fae7b8 @tibbe Add unit test
authored
23
d169845 @tibbe Rename D.Ceason to D.Csv
authored
24 import Data.Csv
1fae7b8 @tibbe Add unit test
authored
25
875fd47 @tibbe Add round-trip unit tests
authored
26 ------------------------------------------------------------------------
27 -- Parse tests
28
90bbf52 @tibbe Refactor tests to be more readable
authored
29 decodesAs :: BL.ByteString -> [[B.ByteString]] -> Assertion
743d226 @tibbe Fix bug in custom field delimiter parsing
authored
30 decodesAs input expected = assertResult input expected $ decode input
31
32 decodesWithAs :: DecodeOptions -> BL.ByteString -> [[B.ByteString]] -> Assertion
33 decodesWithAs opts input expected =
34 assertResult input expected $ decodeWith opts input
35
36 assertResult :: BL.ByteString -> [[B.ByteString]]
37 -> Either String (V.Vector (V.Vector B.ByteString)) -> Assertion
38 assertResult input expected res = case res of
c528fe9 @tibbe Add test for embedded newlines and quoted quotes
authored
39 Right r -> V.fromList (map V.fromList expected) @=? r
da2cd94 @tibbe Clean up tests
authored
40 Left err -> assertFailure $
41 " input: " ++ show (BL8.unpack input) ++ "\n" ++
42 "parse error: " ++ err
1fae7b8 @tibbe Add unit test
authored
43
90bbf52 @tibbe Refactor tests to be more readable
authored
44 encodesAs :: [[B.ByteString]] -> BL.ByteString -> Assertion
45 encodesAs input expected =
8f87940 @tibbe Implement encodeByHeader and fix a bug in escape
authored
46 encode (V.fromList (map V.fromList input)) @?= expected
47
fe8fba2 @tibbe Add test for encoding with custom delimiter
authored
48 encodesWithAs :: EncodeOptions -> [[B.ByteString]] -> BL.ByteString -> Assertion
49 encodesWithAs opts input expected =
50 encodeWith opts (V.fromList (map V.fromList input)) @?= expected
51
dd3dda1 @tibbe encodeByHeader: Fix bug where header was omitted
authored
52 namedEncodesAs :: [B.ByteString] -> [[(B.ByteString, B.ByteString)]]
53 -> BL.ByteString -> Assertion
54 namedEncodesAs hdr input expected =
6f22b50 @tibbe Rename byHeader functions to byName
authored
55 encodeByName (V.fromList hdr)
bc4db86 @tibbe Recreate a file that was accidentally deleted
authored
56 (V.fromList $ map HM.fromList input) @?= expected
dd3dda1 @tibbe encodeByHeader: Fix bug where header was omitted
authored
57
87a4daf @tibbe Add tests for name-based decoding
authored
58 namedDecodesAs :: BL.ByteString -> [B.ByteString]
59 -> [[(B.ByteString, B.ByteString)]] -> Assertion
6f22b50 @tibbe Rename byHeader functions to byName
authored
60 namedDecodesAs input ehdr expected = case decodeByName input of
87a4daf @tibbe Add tests for name-based decoding
authored
61 Right r -> (V.fromList ehdr, expected') @=? r
62 Left err -> assertFailure $
63 " input: " ++ show (BL8.unpack input) ++ "\n" ++
64 "parse error: " ++ err
65 where
bc4db86 @tibbe Recreate a file that was accidentally deleted
authored
66 expected' = V.fromList $ map HM.fromList expected
87a4daf @tibbe Add tests for name-based decoding
authored
67
c528fe9 @tibbe Add test for embedded newlines and quoted quotes
authored
68 testRfc4180 :: Assertion
90bbf52 @tibbe Refactor tests to be more readable
authored
69 testRfc4180 = (BL8.pack $
c528fe9 @tibbe Add test for embedded newlines and quoted quotes
authored
70 "#field1,field2,field3\n" ++
a334a56 @tibbe Filter blank lines and add more tests
authored
71 "\"aaa\",\"bb\n" ++
c528fe9 @tibbe Add test for embedded newlines and quoted quotes
authored
72 "b\",\"ccc\"\n" ++
73 "\"a,a\",\"b\"\"bb\",\"ccc\"\n" ++
74 "zzz,yyy,xxx\n")
90bbf52 @tibbe Refactor tests to be more readable
authored
75 `decodesAs`
c528fe9 @tibbe Add test for embedded newlines and quoted quotes
authored
76 [["#field1", "field2", "field3"],
77 ["aaa", "bb\nb", "ccc"],
78 ["a,a", "b\"bb", "ccc"],
79 ["zzz", "yyy", "xxx"]]
80
3632111 @tibbe Refactor tests
authored
81 positionalTests :: [TF.Test]
82 positionalTests =
01cd3a2 @tibbe Fix typo
authored
83 [ testGroup "encode" $ map encodeTest
f41607c @tibbe More test refactoring
authored
84 [ ("simple", [["abc"]], "abc\r\n")
85 , ("quoted", [["\"abc\""]], "\"\"\"abc\"\"\"\r\n")
86 , ("quote", [["a\"b"]], "\"a\"\"b\"\r\n")
87 , ("quotedQuote", [["\"a\"b\""]], "\"\"\"a\"\"b\"\"\"\r\n")
88 , ("leadingSpace", [[" abc"]], "\" abc\"\r\n")
89 , ("comma", [["abc,def"]], "\"abc,def\"\r\n")
90 , ("twoFields", [["abc","def"]], "abc,def\r\n")
91 , ("twoRecords", [["abc"], ["def"]], "abc\r\ndef\r\n")
92 , ("newline", [["abc\ndef"]], "\"abc\ndef\"\r\n")
8f87940 @tibbe Implement encodeByHeader and fix a bug in escape
authored
93 ]
fe8fba2 @tibbe Add test for encoding with custom delimiter
authored
94 , testGroup "encodeWith"
95 [ testCase "tab-delim" $ encodesWithAs (defEnc { encDelimiter = 9 })
96 [["1", "2"]] "1\t2\r\n"
97 ]
3632111 @tibbe Refactor tests
authored
98 , testGroup "decode" $ map decodeTest
99 [ ("simple", "a,b,c\n", [["a", "b", "c"]])
100 , ("crlf", "a,b\r\nc,d\r\n", [["a", "b"], ["c", "d"]])
101 , ("noEol", "a,b,c", [["a", "b", "c"]])
102 , ("blankLine", "a,b,c\n\nd,e,f\n\n",
103 [["a", "b", "c"], ["d", "e", "f"]])
104 , ("leadingSpace", " a, b, c\n", [[" a", " b", " c"]])
105 ] ++ [testCase "rfc4180" testRfc4180]
743d226 @tibbe Fix bug in custom field delimiter parsing
authored
106 , testGroup "decodeWith"
fe8fba2 @tibbe Add test for encoding with custom delimiter
authored
107 [ testCase "tab-delim" $ decodesWithAs (defDec { decDelimiter = 9 })
108 "1\t2" [["1", "2"]]
743d226 @tibbe Fix bug in custom field delimiter parsing
authored
109 ]
2c3454e @tibbe Fix parsing bugs
authored
110 ]
f41607c @tibbe More test refactoring
authored
111 where
112 encodeTest (name, input, expected) =
113 testCase name $ input `encodesAs` expected
3632111 @tibbe Refactor tests
authored
114 decodeTest (name, input, expected) =
115 testCase name $ input `decodesAs` expected
fe8fba2 @tibbe Add test for encoding with custom delimiter
authored
116 defEnc = defaultEncodeOptions
117 defDec = defaultDecodeOptions
f41607c @tibbe More test refactoring
authored
118
119 nameBasedTests :: [TF.Test]
87a4daf @tibbe Add tests for name-based decoding
authored
120 nameBasedTests =
121 [ testGroup "encode" $ map encodeTest
122 [ ("simple", ["field"], [[("field", "abc")]], "field\r\nabc\r\n")
123 , ("twoFields", ["field1", "field2"],
124 [[("field1", "abc"), ("field2", "def")]],
125 "field1,field2\r\nabc,def\r\n")
126 , ("twoRecords", ["field"], [[("field", "abc")], [("field", "def")]],
127 "field\r\nabc\r\ndef\r\n")
128 ]
129 , testGroup "decode" $ map decodeTest
130 [("simple", "field\r\nabc\r\n", ["field"], [[("field", "abc")]])
131 , ("twoFields", "field1,field2\r\nabc,def\r\n", ["field1", "field2"],
132 [[("field1", "abc"), ("field2", "def")]])
133 , ("twoRecords", "field\r\nabc\r\ndef\r\n", ["field"],
134 [[("field", "abc")], [("field", "def")]])
135 ]
dd3dda1 @tibbe encodeByHeader: Fix bug where header was omitted
authored
136 ]
137 where
138 encodeTest (name, hdr, input, expected) =
139 testCase name $ namedEncodesAs hdr input expected
87a4daf @tibbe Add tests for name-based decoding
authored
140 decodeTest (name, input, hdr, expected) =
141 testCase name $ namedDecodesAs input hdr expected
1fae7b8 @tibbe Add unit test
authored
142
875fd47 @tibbe Add round-trip unit tests
authored
143 ------------------------------------------------------------------------
144 -- Conversion tests
145
146 instance Arbitrary B.ByteString where
147 arbitrary = B.pack `fmap` arbitrary
148
149 instance Arbitrary BL.ByteString where
150 arbitrary = BL.fromChunks `fmap` arbitrary
151
152 instance Arbitrary T.Text where
153 arbitrary = T.pack `fmap` arbitrary
154
155 instance Arbitrary LT.Text where
156 arbitrary = LT.fromChunks `fmap` arbitrary
157
74b3f35 @tibbe Fix test bug
authored
158 -- A single column with an empty string is indistinguishable from an
159 -- empty line (which we will ignore.) We therefore encode at least two
160 -- columns.
875fd47 @tibbe Add round-trip unit tests
authored
161 roundTrip :: (Eq a, FromField a, ToField a) => a -> Bool
74b3f35 @tibbe Fix test bug
authored
162 roundTrip x = case decode (encode (V.singleton (x, dummy))) of
163 Right v | V.length v == 1 -> let (y, _ :: Char) = v ! 0 in x == y
164 _ -> False
165 where dummy = 'a'
166
80d4bcc @tibbe Add test for bounday conditions
authored
167 boundary :: forall a. (Bounded a, Eq a, FromField a, ToField a) => a -> Bool
168 boundary _dummy = roundTrip (minBound :: a) && roundTrip (maxBound :: a)
169
74b3f35 @tibbe Fix test bug
authored
170 -- TODO: Right now we only encode ASCII properly. Should we support
171 -- UTF-8? Arbitrary byte strings?
875fd47 @tibbe Add round-trip unit tests
authored
172
173 conversionTests :: [TF.Test]
174 conversionTests =
3632111 @tibbe Refactor tests
authored
175 [ testGroup "roundTrip"
176 [ testProperty "Char" (roundTrip :: Char -> Bool)
177 , testProperty "ByteString" (roundTrip :: B.ByteString -> Bool)
178 , testProperty "Int" (roundTrip :: Int -> Bool)
179 , testProperty "Integer" (roundTrip :: Integer -> Bool)
180 , testProperty "Int8" (roundTrip :: Int8 -> Bool)
181 , testProperty "Int16" (roundTrip :: Int16 -> Bool)
182 , testProperty "Int32" (roundTrip :: Int32 -> Bool)
183 , testProperty "Int64" (roundTrip :: Int64 -> Bool)
184 , testProperty "Word" (roundTrip :: Word -> Bool)
185 , testProperty "Word8" (roundTrip :: Word8 -> Bool)
186 , testProperty "Word16" (roundTrip :: Word16 -> Bool)
187 , testProperty "Word32" (roundTrip :: Word32 -> Bool)
188 , testProperty "Word64" (roundTrip :: Word64 -> Bool)
189 , testProperty "lazy ByteString"
190 (roundTrip :: BL.ByteString -> Bool)
191 , testProperty "Text" (roundTrip :: T.Text -> Bool)
192 , testProperty "lazy Text" (roundTrip :: LT.Text -> Bool)
193 ]
194 , testGroup "boundary"
195 [ testProperty "Int" (boundary (undefined :: Int))
196 , testProperty "Int8" (boundary (undefined :: Int8))
197 , testProperty "Int16" (boundary (undefined :: Int16))
198 , testProperty "Int32" (boundary (undefined :: Int32))
199 , testProperty "Int64" (boundary (undefined :: Int64))
200 , testProperty "Word" (boundary (undefined :: Word))
201 , testProperty "Word8" (boundary (undefined :: Word8))
202 , testProperty "Word16" (boundary (undefined :: Word16))
203 , testProperty "Word32" (boundary (undefined :: Word32))
204 , testProperty "Word64" (boundary (undefined :: Word64))
205 ]
875fd47 @tibbe Add round-trip unit tests
authored
206 ]
207
208 ------------------------------------------------------------------------
209 -- Test harness
210
211 allTests :: [TF.Test]
3632111 @tibbe Refactor tests
authored
212 allTests = [ testGroup "positional" positionalTests
213 , testGroup "named" nameBasedTests
214 , testGroup "conversion" conversionTests
215 ]
875fd47 @tibbe Add round-trip unit tests
authored
216
1fae7b8 @tibbe Add unit test
authored
217 main :: IO ()
218 main = defaultMain allTests
Something went wrong with that request. Please try again.