on master help sign in
Add test cases for Data.Fold/Array, FileSystem.Handle
0bd1586Ranjeet Ranjan
streamly.cabal+1
@@ -168,6 +168,7 @@ extra-source-files:
168 test/Streamly/Test/Data/Array/Storable/Foreign.hs
168 test/Streamly/Test/Data/Array/Storable/Foreign.hs
169 test/Streamly/Test/Data/Parser/ParserD.hs
169 test/Streamly/Test/Data/Parser/ParserD.hs
170 test/Streamly/Test/FileSystem/Event.hs
170 test/Streamly/Test/FileSystem/Event.hs
171+ test/Streamly/Test/FileSystem/Handle.hs
171 test/Streamly/Test/Prelude.hs
172 test/Streamly/Test/Prelude.hs
172 test/Streamly/Test/Prelude/*.hs
173 test/Streamly/Test/Prelude/*.hs
173 test/Streamly/Test/Unicode/Stream.hs
174 test/Streamly/Test/Unicode/Stream.hs
test/Streamly/Test/Common/Array.hs+22−2
@@ -16,12 +16,10 @@ import Foreign.Storable (Storable(..))
16import Test.Hspec.QuickCheck
16import Test.Hspec.QuickCheck
17import Test.QuickCheck (Property, forAll, Gen, vectorOf, arbitrary, choose)
17import Test.QuickCheck (Property, forAll, Gen, vectorOf, arbitrary, choose)
18import Test.QuickCheck.Monadic (monadicIO, assert, run)
18import Test.QuickCheck.Monadic (monadicIO, assert, run)
19−
20import Test.Hspec as H
19import Test.Hspec as H
21
20
22import Streamly.Prelude (SerialT)
21import Streamly.Prelude (SerialT)
23import qualified Streamly.Prelude as S
22import qualified Streamly.Prelude as S
24−
25#ifdef TEST_SMALL_ARRAY
23#ifdef TEST_SMALL_ARRAY
26import qualified Streamly.Internal.Data.SmallArray as A
24import qualified Streamly.Internal.Data.SmallArray as A
27type Array = A.SmallArray
25type Array = A.SmallArray
@@ -125,12 +123,31 @@ testFromStreamNUnfold = genericTestFromTo A.fromStreamN (S.unfold A.read) (==)
125testFromStreamNToStream :: Property
123testFromStreamNToStream :: Property
126testFromStreamNToStream = genericTestFromTo A.fromStreamN A.toStream (==)
124testFromStreamNToStream = genericTestFromTo A.fromStreamN A.toStream (==)
127
125
126+testFromListN :: Property
127+testFromListN =
128+ forAll (choose (0, maxArrLen)) $ \len ->
129+ forAll (choose (0, len)) $ \n ->
130+ forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
131+ monadicIO $ do
132+ let arr = A.fromListN n list
133+ xs <- run $ S.toList $ (S.unfold A.read) arr
134+ assert (xs == take n list)
135+
128#ifndef TEST_SMALL_ARRAY
136#ifndef TEST_SMALL_ARRAY
129testFromStreamToStream :: Property
137testFromStreamToStream :: Property
130testFromStreamToStream = genericTestFromTo (const A.fromStream) A.toStream (==)
138testFromStreamToStream = genericTestFromTo (const A.fromStream) A.toStream (==)
131
139
132testFoldUnfold :: Property
140testFoldUnfold :: Property
133testFoldUnfold = genericTestFromTo (const (S.fold A.write)) (S.unfold A.read) (==)
141testFoldUnfold = genericTestFromTo (const (S.fold A.write)) (S.unfold A.read) (==)
142+
143+testFromList :: Property
144+testFromList =
145+ forAll (choose (0, maxArrLen)) $ \len ->
146+ forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
147+ monadicIO $ do
148+ let arr = A.fromList list
149+ xs <- run $ S.toList $ (S.unfold A.read) arr
150+ assert (xs == list)
134#endif
151#endif
135
152
136#if defined(TEST_ARRAY) ||\
153#if defined(TEST_ARRAY) ||\
@@ -202,10 +219,13 @@ main =
202 prop "toStreamRev . writeN === reverse" testFoldNToStreamRev
219 prop "toStreamRev . writeN === reverse" testFoldNToStreamRev
203 prop "read . fromStreamN === id" testFromStreamNUnfold
220 prop "read . fromStreamN === id" testFromStreamNUnfold
204 prop "toStream . fromStreamN === id" testFromStreamNToStream
221 prop "toStream . fromStreamN === id" testFromStreamNToStream
222+ prop "First N elements of a list" testFromListN
223+
205#ifndef TEST_SMALL_ARRAY
224#ifndef TEST_SMALL_ARRAY
206 prop "length . fromStream === n" testLengthFromStream
225 prop "length . fromStream === n" testLengthFromStream
207 prop "toStream . fromStream === id" testFromStreamToStream
226 prop "toStream . fromStream === id" testFromStreamToStream
208 prop "read . write === id" testFoldUnfold
227 prop "read . write === id" testFoldUnfold
228+ prop "From a list" testFromList
209#endif
229#endif
210
230
211#if defined(TEST_ARRAY) ||\
231#if defined(TEST_ARRAY) ||\
test/Streamly/Test/Data/Fold.hs+271−72
@@ -1,16 +1,31 @@
1module Main (main) where
1module Main (main) where
2
2
3+import Prelude hiding
4+ (maximum, minimum, elem, notElem, null, product, sum, head, last)
5+import Data.Semigroup (Sum(..), getSum)
6+import Test.QuickCheck
7+ ( Gen
8+ , Property
9+ , arbitrary
10+ , choose
11+ , forAll
12+ , listOf
13+ , listOf1
14+ , property
15+ , vectorOf
16+ , withMaxSuccess
17+ )
18+import Prelude hiding
19+ (maximum, minimum, elem, notElem, null, product, sum, head, last)
3import Streamly.Test.Common (checkListEqual)
20import Streamly.Test.Common (checkListEqual)
4−import Prelude hiding (maximum, minimum, elem, notElem, null, product, sum, head, last)
21+import Test.QuickCheck.Monadic (monadicIO, assert, run)
22+
5import qualified Streamly.Internal.Data.Fold as F
23import qualified Streamly.Internal.Data.Fold as F
6import qualified Streamly.Prelude as S
24import qualified Streamly.Prelude as S
7import qualified Streamly.Data.Fold as FL
25import qualified Streamly.Data.Fold as FL
8
26
9import Test.Hspec as H
27import Test.Hspec as H
10import Test.Hspec.QuickCheck
28import Test.Hspec.QuickCheck
11−import Test.QuickCheck
12− (Property, property, forAll, Gen, vectorOf, arbitrary, choose, listOf)
13−import Test.QuickCheck.Monadic (monadicIO, assert, run)
14
29
15maxStreamLen :: Int
30maxStreamLen :: Int
16maxStreamLen = 1000
31maxStreamLen = 1000
@@ -43,75 +58,75 @@ rollingHashFirstN =
43 b <- run $ S.fold (F.rollingHashFirstN n) $ S.fromList vec
58 b <- run $ S.fold (F.rollingHashFirstN n) $ S.fromList vec
44 assert $ a == b
59 assert $ a == b
45
60
46−
47head :: [Int] -> Expectation
61head :: [Int] -> Expectation
48head ls = S.fold FL.head (S.fromList ls) `shouldReturn` headl ls
62head ls = S.fold FL.head (S.fromList ls) `shouldReturn` headl ls
49− where
50− headl [] = Nothing
51− headl (x:_) = Just x
52
63
64+ where
65+
66+ headl [] = Nothing
67+ headl (x:_) = Just x
53
68
54length :: [Int] -> Expectation
69length :: [Int] -> Expectation
55length ls = S.fold FL.length (S.fromList ls) `shouldReturn` Prelude.length ls
70length ls = S.fold FL.length (S.fromList ls) `shouldReturn` Prelude.length ls
56
71
57−
58sum :: [Int] -> Expectation
72sum :: [Int] -> Expectation
59sum ls = S.fold FL.sum (S.fromList ls) `shouldReturn` foldl (+) 0 ls
73sum ls = S.fold FL.sum (S.fromList ls) `shouldReturn` foldl (+) 0 ls
60
74
61−
62product :: [Int] -> Expectation
75product :: [Int] -> Expectation
63product ls = S.fold FL.product (S.fromList ls) `shouldReturn` foldl (*) 1 ls
76product ls = S.fold FL.product (S.fromList ls) `shouldReturn` foldl (*) 1 ls
64
77
65−
66lesser :: (a -> a -> Ordering) -> a -> a -> a
78lesser :: (a -> a -> Ordering) -> a -> a -> a
67lesser f x y = if f x y == LT then x else y
79lesser f x y = if f x y == LT then x else y
68
80
69−
70greater :: (a -> a -> Ordering) -> a -> a -> a
81greater :: (a -> a -> Ordering) -> a -> a -> a
71greater f x y = if f x y == GT then x else y
82greater f x y = if f x y == GT then x else y
72
83
73−
74foldMaybe :: (b -> a -> b) -> b -> [a] -> Maybe b
84foldMaybe :: (b -> a -> b) -> b -> [a] -> Maybe b
75foldMaybe f acc ls =
85foldMaybe f acc ls =
76− case ls of
86+ case ls of
77− [] -> Nothing
87+ [] -> Nothing
78− _ -> Just (foldl f acc ls)
88+ _ -> Just (foldl f acc ls)
79−
80
89
81maximumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
90maximumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
91+maximumBy genmin f ls =
82−maximumBy genmin f ls = S.fold (FL.maximumBy f) (S.fromList ls) `shouldReturn` foldMaybe (greater f) genmin ls
92+ S.fold (FL.maximumBy f) (S.fromList ls)
83−
93+ `shouldReturn`
94+ foldMaybe (greater f) genmin ls
84
95
85maximum :: (Show a, Ord a) => a -> [a] -> Expectation
96maximum :: (Show a, Ord a) => a -> [a] -> Expectation
97+maximum genmin ls =
98+ S.fold FL.maximum (S.fromList ls)
86−maximum genmin ls = S.fold FL.maximum (S.fromList ls) `shouldReturn` foldMaybe (greater compare) genmin ls
99+ `shouldReturn` foldMaybe (greater compare) genmin ls
87−
88
100
89minimumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
101minimumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
102+minimumBy genmax f ls =
103+ S.fold (FL.minimumBy f) (S.fromList ls)
90−minimumBy genmax f ls = S.fold (FL.minimumBy f) (S.fromList ls) `shouldReturn` foldMaybe (lesser f) genmax ls
104+ `shouldReturn` foldMaybe (lesser f) genmax ls
91−
92
105
93minimum :: (Show a, Ord a) => a -> [a] -> Expectation
106minimum :: (Show a, Ord a) => a -> [a] -> Expectation
107+minimum genmax ls =
108+ S.fold FL.minimum (S.fromList ls)
94−minimum genmax ls = S.fold FL.minimum (S.fromList ls) `shouldReturn` foldMaybe (lesser compare) genmax ls
109+ `shouldReturn` foldMaybe (lesser compare) genmax ls
95−
96
110
97toList :: [Int] -> Expectation
111toList :: [Int] -> Expectation
98toList ls = S.fold FL.toList (S.fromList ls) `shouldReturn` ls
112toList ls = S.fold FL.toList (S.fromList ls) `shouldReturn` ls
99
113
100−
101safeLast :: [a] -> Maybe a
114safeLast :: [a] -> Maybe a
102safeLast [] = Nothing
115safeLast [] = Nothing
103safeLast (x:[]) = Just x
116safeLast (x:[]) = Just x
104safeLast (_:xs) = safeLast xs
117safeLast (_:xs) = safeLast xs
105
118
106−
107last :: [String] -> Expectation
119last :: [String] -> Expectation
108last ls = S.fold FL.last (S.fromList ls) `shouldReturn` safeLast ls
120last ls = S.fold FL.last (S.fromList ls) `shouldReturn` safeLast ls
109
121
110mapMaybe :: [Int] -> Expectation
122mapMaybe :: [Int] -> Expectation
111mapMaybe ls =
123mapMaybe ls =
112− (let f = F.mapMaybe (\x -> if even x then Just x else Nothing) FL.toList
124+ let maybeEven x =
113− in S.fold f (S.fromList ls))
125+ if even x
126+ then Just x
127+ else Nothing
128+ f = F.mapMaybe maybeEven FL.toList
114− `shouldReturn` filter even ls
129+ in S.fold f (S.fromList ls) `shouldReturn` filter even ls
115
130
116nth :: Int -> [a] -> Maybe a
131nth :: Int -> [a] -> Maybe a
117nth idx (x:xs) = if idx == 0
132nth idx (x:xs) = if idx == 0
@@ -121,48 +136,52 @@ nth idx (x:xs) = if idx == 0
121 else nth (idx - 1) xs
136 else nth (idx - 1) xs
122nth _ [] = Nothing
137nth _ [] = Nothing
123
138
124−
125index :: Int -> [String] -> Expectation
139index :: Int -> [String] -> Expectation
126index idx ls = let x = S.fold (FL.index idx) (S.fromList ls)
140index idx ls = let x = S.fold (FL.index idx) (S.fromList ls)
127 in x `shouldReturn` (nth idx ls)
141 in x `shouldReturn` (nth idx ls)
128
142
129−
130find :: (Show a, Eq a) => (a -> Bool) -> [a] -> Expectation
143find :: (Show a, Eq a) => (a -> Bool) -> [a] -> Expectation
131find f ls = do
144find f ls = do
132− let x = S.fold (FL.findIndex f) (S.fromList ls)
145+ y <- S.fold (FL.findIndex f) (S.fromList ls)
133− y <- x
134− case y of
146+ case y of
147+ Nothing ->
135− Nothing -> S.fold (FL.find f) (S.fromList ls) `shouldReturn` Nothing
148+ let fld = S.fold (FL.find f) (S.fromList ls)
149+ in fld `shouldReturn` Nothing
150+ Just idx ->
136− Just idx -> S.fold (FL.any f) (S.fromList $ take idx ls) `shouldReturn` False
151+ let fld = S.fold (FL.any f) (S.fromList $ take idx ls)
137−
152+ in fld `shouldReturn` False
138
153
139neg :: (a -> Bool) -> a -> Bool
154neg :: (a -> Bool) -> a -> Bool
140neg f x = if f x == True then False else True
155neg f x = if f x == True then False else True
141
156
142findIndex :: (a -> Bool) -> [a] -> Expectation
157findIndex :: (a -> Bool) -> [a] -> Expectation
143findIndex f ls = do
158findIndex f ls = do
144− let x = S.fold (FL.findIndex f) (S.fromList ls)
159+ y <- S.fold (FL.findIndex f) (S.fromList ls)
145− y <- x
146− case y of
160+ case y of
161+ Nothing ->
147− Nothing -> S.fold (FL.all $ neg f) (S.fromList ls) `shouldReturn` True
162+ let fld = S.fold (FL.all $ neg f) (S.fromList ls)
163+ in fld `shouldReturn` True
148− Just idx -> if idx == 0
164+ Just idx -> if idx == 0
165+ then
149− then S.fold (FL.all f) (S.fromList []) `shouldReturn` True
166+ S.fold (FL.all f) (S.fromList []) `shouldReturn` True
167+ else
150− else S.fold (FL.all f) (S.fromList $ (take idx ls)) `shouldReturn` False
168+ S.fold (FL.all f) (S.fromList $ (take idx ls))
169+ `shouldReturn`
170+ False
151
171
152predicate :: Int -> Bool
172predicate :: Int -> Bool
153predicate x = if x * x < 100 then True else False
173predicate x = if x * x < 100 then True else False
154
174
155elemIndex :: Int -> [Int] -> Expectation
175elemIndex :: Int -> [Int] -> Expectation
156elemIndex elm ls = do
176elemIndex elm ls = do
157− let x = S.fold (FL.elemIndex elm) (S.fromList ls)
177+ y <- S.fold (FL.elemIndex elm) (S.fromList ls)
158− y <- x
159− case y of
178+ case y of
179+ Nothing ->
160− Nothing -> S.fold (FL.any (\z -> if z == elm
180+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
161− then True
162− else False)) (S.fromList ls) `shouldReturn` False
181+ in fld `shouldReturn` False
163− Just idx -> S.fold (FL.any (\z -> if z == elm
182+ Just idx ->
164− then True
183+ let fld = S.fold (FL.any (== elm)) (S.fromList $ (take idx ls))
165− else False)) (S.fromList $ (take idx ls)) `shouldReturn` False
184+ in fld `shouldReturn` False
166
185
167null :: [Int] -> Expectation
186null :: [Int] -> Expectation
168null ls = S.fold FL.null (S.fromList ls) `shouldReturn` case ls of
187null ls = S.fold FL.null (S.fromList ls) `shouldReturn` case ls of
@@ -170,36 +189,26 @@ null ls = S.fold FL.null (S.fromList ls) `shouldReturn` case ls of
170 _ -> False
189 _ -> False
171elem :: Int -> [Int] -> Expectation
190elem :: Int -> [Int] -> Expectation
172elem elm ls = do
191elem elm ls = do
173− let x = S.fold (FL.elem elm) (S.fromList ls)
192+ y <- S.fold (FL.elem elm) (S.fromList ls)
174− y <- x
193+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
175− S.fold (FL.any (\z -> if z == elm
176− then True
177− else False)) (S.fromList ls) `shouldReturn` y
194+ fld `shouldReturn` y
178−
179
195
180notElem :: Int -> [Int] -> Expectation
196notElem :: Int -> [Int] -> Expectation
181notElem elm ls = do
197notElem elm ls = do
182− let x = S.fold (FL.notElem elm) (S.fromList ls)
198+ y <- S.fold (FL.notElem elm) (S.fromList ls)
183− y <- x
199+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
184− S.fold (FL.any (\z -> if z == elm
185− then True
186− else False)) (S.fromList ls) `shouldReturn` (if y == True
200+ fld `shouldReturn` not y
187− then False
188− else True)
189−
190
201
191all :: (a -> Bool) -> [a] -> Expectation
202all :: (a -> Bool) -> [a] -> Expectation
203+all f ls =
192−all f ls = S.fold (FL.all f) (S.fromList ls) `shouldReturn` Prelude.and (map f ls)
204+ S.fold (FL.all f) (S.fromList ls) `shouldReturn` Prelude.and (map f ls)
193−
194
205
195any :: (a -> Bool) -> [a] -> Expectation
206any :: (a -> Bool) -> [a] -> Expectation
196any f ls = S.fold (FL.any f) (S.fromList ls) `shouldReturn` Prelude.any f ls
207any f ls = S.fold (FL.any f) (S.fromList ls) `shouldReturn` Prelude.any f ls
197
208
198−
199and :: [Bool] -> Expectation
209and :: [Bool] -> Expectation
200and ls = S.fold FL.and (S.fromList ls) `shouldReturn` Prelude.and ls
210and ls = S.fold FL.and (S.fromList ls) `shouldReturn` Prelude.and ls
201
211
202−
203or :: [Bool] -> Expectation
212or :: [Bool] -> Expectation
204or ls = S.fold FL.or (S.fromList ls) `shouldReturn` Prelude.or ls
213or ls = S.fold FL.or (S.fromList ls) `shouldReturn` Prelude.or ls
205
214
@@ -230,6 +239,181 @@ sliceSepByMax =
230 Right xs -> checkListEqual xs ys
239 Right xs -> checkListEqual xs ys
231 Left _ -> property False
240 Left _ -> property False
232
241
242+chooseFloat :: (Float, Float) -> Gen Float
243+chooseFloat = choose
244+
245+drain :: [Int] -> Expectation
246+drain ls = S.fold FL.drain (S.fromList ls) `shouldReturn` ()
247+
248+drainBy :: [Int] -> Expectation
249+drainBy ls = S.fold (FL.drainBy return) (S.fromList ls) `shouldReturn` ()
250+
251+mean :: Property
252+mean =
253+ forAll (listOf1 (chooseFloat (-100.0, 100.0)))
254+ $ \ls0 -> withMaxSuccess 1000 $ monadicIO $ action ls0
255+
256+ where
257+
258+ action ls = do
259+ v1 <- run $ S.fold FL.mean (S.fromList ls)
260+ let v2 = (foldl (+) 0 ls) / fromIntegral (Prelude.length ls)
261+ assert (abs (v1 - v2) < 0.0001)
262+
263+stdDev :: Property
264+stdDev =
265+ forAll (listOf1 (chooseFloat (-100.0, 100.0)))
266+ $ \ls0 -> withMaxSuccess 1000 $ monadicIO $ action ls0
267+
268+ where
269+
270+ action ls = do
271+ v1 <- run $ S.fold FL.stdDev (S.fromList ls)
272+ let avg = (foldl (+) 0 ls) / fromIntegral (Prelude.length ls)
273+ se = (foldl (+) 0 (map (\x -> (x - avg) * (x - avg)) ls))
274+ sd = sqrt $ se / fromIntegral (Prelude.length ls)
275+ assert (abs (v1 - sd) < 0.0001 )
276+
277+variance :: Property
278+variance =
279+ forAll (listOf1 (chooseFloat (-100.0, 100.0)))
280+ $ \ls0 -> withMaxSuccess 1000 $ monadicIO $ action ls0
281+
282+ where
283+
284+ action ls = do
285+ v1 <- run $ S.fold FL.variance (S.fromList ls)
286+ let avg = (foldl (+) 0 ls) / fromIntegral (Prelude.length ls)
287+ se = (foldl (+) 0 (map (\x -> (x - avg) * (x - avg)) ls))
288+ vr = se / fromIntegral (Prelude.length ls)
289+ assert (abs (v1 - vr) < 0.01 )
290+
291+mconcat :: Property
292+mconcat =
293+ forAll (listOf1 (chooseInt (intMin, intMax)))
294+ $ \ls0 -> monadicIO $ action ls0
295+
296+ where
297+
298+ action ls = do
299+ v1 <- run $ S.fold FL.mconcat (S.map Sum $ S.fromList ls)
300+ let v2 = (foldl (+) 0 ls)
301+ assert (getSum v1 == v2)
302+
303+foldMap :: Property
304+foldMap =
305+ forAll (listOf1 (chooseInt (intMin, intMax)))
306+ $ \ls0 -> monadicIO $ action ls0
307+
308+ where
309+
310+ action ls = do
311+ v1 <- run $ S.fold (FL.foldMap Sum) $ S.fromList ls
312+ let v2 = (foldl (+) 0 ls)
313+ assert (getSum v1 == v2)
314+
315+foldMapM :: Property
316+foldMapM =
317+ forAll (listOf1 (chooseInt (intMin, intMax)))
318+ $ \ls0 -> monadicIO $ action ls0
319+
320+ where
321+
322+ action ls = do
323+ v1 <- run $ S.fold (FL.foldMapM (return . Sum)) $ S.fromList ls
324+ let v2 = (foldl (+) 0 ls)
325+ assert (getSum v1 == v2)
326+
327+lookup :: Property
328+lookup =
329+ forAll (chooseInt (1, 15))
330+ $ \key0 ->monadicIO $ action key0
331+
332+ where
333+
334+ action key = do
335+ let ls = [(1, "first"), (2, "second"), (3, "third"), (4, "fourth")
336+ , (5, "fifth"), (6, "fifth+first"), (7, "fifth+second")
337+ , (8, "fifth+third"), (9, "fifth+fourth")
338+ , (10, "fifth+fifth")]
339+ v1 <- run $ S.fold (FL.lookup key) $ S.fromList ls
340+ let v2 = Prelude.lookup key ls
341+ assert (v1 == v2)
342+
343+mapM :: Property
344+mapM =
345+ forAll (listOf1 (chooseInt (intMin, intMax)))
346+ $ \ls0 -> monadicIO $ action ls0
347+
348+ where
349+
350+ action ls = do
351+ let addLen x = return $ x + Prelude.length ls
352+ fld = FL.mapM addLen FL.sum
353+ v2 = foldl (+) (Prelude.length ls) ls
354+ v1 <- run $ S.fold fld $ S.fromList ls
355+ assert (v1 == v2)
356+
357+teeWithLength :: Property
358+teeWithLength =
359+ forAll (listOf1 (chooseInt (intMin, intMax)))
360+ $ \ls0 -> monadicIO $ action ls0
361+
362+ where
363+
364+ action ls = do
365+ v1 <- run $ S.fold (FL.tee FL.sum FL.length) $ S.fromList ls
366+ let v2 = foldl (+) 0 ls
367+ v3 = Prelude.length ls
368+ assert (v1 == (v2, v3))
369+
370+teeWithMax :: Property
371+teeWithMax =
372+ forAll (listOf1 (chooseInt (intMin, intMax)))
373+ $ \ls0 -> monadicIO $ action ls0
374+
375+ where
376+
377+ action ls = do
378+ v1 <- run $ S.fold (FL.tee FL.sum FL.maximum) $ S.fromList ls
379+ let v2 = foldl (+) 0 ls
380+ v3 = foldMaybe (greater compare) intMin ls
381+ assert (v1 == (v2, v3))
382+
383+distribute :: Property
384+distribute =
385+ forAll (listOf1 (chooseInt (intMin, intMax)))
386+ $ \ls0 -> monadicIO $ action ls0
387+
388+ where
389+
390+ action ls = do
391+ v1 <- run $ S.fold (FL.distribute [FL.sum, FL.length]) $ S.fromList ls
392+ let v2 = foldl (+) 0 ls
393+ v3 = Prelude.length ls
394+ assert (v1 == [v2, v3])
395+
396+partition :: Property
397+partition =
398+ monadicIO $ do
399+ v1 :: (Int, [String]) <-
400+ run
401+ $ S.fold (FL.partition FL.sum FL.toList)
402+ $ S.fromList
403+ [Left 1, Right "abc", Left 3, Right "xy", Right "pp2"]
404+ let v2 = (4,["abc","xy","pp2"])
405+ assert (v1 == v2)
406+
407+unzip :: Property
408+unzip =
409+ monadicIO $ do
410+ v1 :: (Int, [String]) <-
411+ run
412+ $ S.fold (FL.unzip FL.sum FL.toList)
413+ $ S.fromList [(1, "aa"), (2, "bb"), (3, "cc")]
414+ let v2 = (6, ["aa", "bb", "cc"])
415+ assert (v1 == v2)
416+
233main :: IO ()
417main :: IO ()
234main = hspec $
418main = hspec $
235 describe "Fold s" $ do
419 describe "Fold s" $ do
@@ -259,3 +443,18 @@ main = hspec $
259 prop "ltake" ltake
443 prop "ltake" ltake
260 prop "sliceSepBy" sliceSepBy
444 prop "sliceSepBy" sliceSepBy
261 prop "sliceSepByMax" sliceSepByMax
445 prop "sliceSepByMax" sliceSepByMax
446+ prop "drain" Main.drain
447+ prop "drainBy" Main.drainBy
448+ prop "mean" Main.mean
449+ prop "stdDev" Main.stdDev
450+ prop "variance" Main.variance
451+ prop "mconcat" Main.mconcat
452+ prop "foldMap" Main.foldMap
453+ prop "foldMapM" Main.foldMapM
454+ prop "lookup" Main.lookup
455+ prop "mapM" Main.mapM
456+ prop "teeWithLength" Main.teeWithLength
457+ prop "teeWithMax" Main.teeWithMax
458+ prop "distribute" Main.distribute
459+ prop "partition" Main.partition
460+ prop "unzip" Main.unzip
test/Streamly/Test/FileSystem/Handle.hsnew file+165
@@ -0,0 +1,165 @@
1+-- |
2+-- Module : Streamly.Test.FileSystem.Handle
3+-- Copyright : (c) 2020 Composewell technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Test.FileSystem.Handle (main) where
10+
11+import Data.Functor.Identity (runIdentity)
12+import Data.Word (Word8)
13+import Foreign.Storable (Storable(..))
14+import Streamly.Internal.Data.Stream.IsStream (IsStream, SerialT)
15+import System.FilePath ((</>))
16+import System.IO
17+ ( Handle
18+ , IOMode(..)
19+ , SeekMode(..)
20+ , hClose
21+ , hFlush
22+ , hSeek
23+ , openFile
24+ )
25+import System.IO.Temp (withSystemTempDirectory)
26+import Test.QuickCheck (Property, forAll, Gen, vectorOf, choose)
27+import Test.QuickCheck.Monadic (monadicIO, assert, run)
28+
29+import Streamly.FileSystem.Handle as FH
30+import Test.Hspec as H
31+import Test.Hspec.QuickCheck
32+
33+import qualified Streamly.Data.Fold as FL
34+import qualified Streamly.Internal.Data.Stream.IsStream as S
35+import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
36+import qualified Streamly.Internal.Unicode.Stream as U
37+
38+allocOverhead :: Int
39+allocOverhead = 2 * sizeOf (undefined :: Int)
40+
41+defaultChunkSize :: Int
42+defaultChunkSize = 32 * k - allocOverhead
43+ where k = 1024
44+
45+maxArrLen :: Int
46+maxArrLen = defaultChunkSize * 8
47+
48+maxTestCount :: Int
49+maxTestCount = 10
50+
51+chooseWord8 :: (Word8, Word8) -> Gen Word8
52+chooseWord8 = choose
53+
54+utf8ToString :: A.Array Word8 -> String
55+utf8ToString = runIdentity . S.toList . U.decodeUtf8' . A.toStream
56+
57+testData :: String
58+testData = "This is the test data for FileSystem.Handle ??`!@#$%^&*~~))`]"
59+
60+testDataLarge :: String
61+testDataLarge = concat $ replicate 6000 testData
62+
63+executor :: (Handle -> (SerialT IO Char)) -> IO (SerialT IO Char)
64+executor f =
65+ withSystemTempDirectory "fs_handle" $ \fp -> do
66+ let fpath = fp </> "tmp_read.txt"
67+ writeFile fpath testDataLarge
68+ h <- openFile fpath ReadMode
69+ return $ f h
70+
71+readFromHandle :: IO (SerialT IO Char)
72+readFromHandle =
73+ let f = U.decodeUtf8 . S.unfold FH.read
74+ in executor f
75+
76+readWithBufferFromHandle :: IO (SerialT IO Char)
77+readWithBufferFromHandle =
78+ let f1 = (\h -> (1024, h))
79+ f2 = U.decodeUtf8 . S.unfold FH.readWithBufferOf . f1
80+ in executor f2
81+
82+readChunksFromHandle :: IO (SerialT IO Char)
83+readChunksFromHandle =
84+ let f = U.decodeUtf8 . S.concatMap (A.toStream) . S.unfold FH.readChunks
85+ in executor f
86+
87+readChunksWithBuffer :: IO (SerialT IO Char)
88+readChunksWithBuffer =
89+ let f1 = (\h -> (1024, h))
90+ f2 =
91+ U.decodeUtf8
92+ . S.concatMap (A.toStream)
93+ . S.unfold FH.readChunksWithBufferOf
94+ . f1
95+ in executor f2
96+
97+testRead :: (IsStream t) => IO (t IO Char) -> Property
98+testRead fn = monadicIO $ do
99+ let v2 = (S.fromList testDataLarge)
100+ v1 <- run $ fn
101+ res <- run $ S.eqBy (==) v1 v2
102+ assert (res)
103+
104+testWrite :: (Handle -> FL.Fold IO Word8 ()) -> Property
105+testWrite hfold =
106+ forAll (choose (0, maxArrLen)) $ \len ->
107+ forAll (vectorOf len $ chooseWord8 (0, 255)) $ \list0 ->
108+ monadicIO $ do
109+ res <- run $ go list0
110+ assert res
111+
112+ where
113+
114+ go list =
115+ withSystemTempDirectory "fs_handle" $ \fp -> do
116+ let fpathWrite = fp </> "tmp_write.txt"
117+ writeFile fpathWrite ""
118+ h <- openFile fpathWrite ReadWriteMode
119+ hSeek h AbsoluteSeek 0
120+ S.fold (hfold h) $ S.fromList list
121+ hFlush h
122+ hSeek h AbsoluteSeek 0
123+ ls <- S.toList $ S.unfold FH.read h
124+ hClose h
125+ return (ls == list)
126+
127+testWriteWithChunk :: Property
128+testWriteWithChunk =
129+ monadicIO $ do
130+ res <- run $ go
131+ assert res
132+
133+ where
134+
135+ go =
136+ withSystemTempDirectory "fs_handle" $ \fp -> do
137+ let fpathRead = fp </> "tmp_read.txt"
138+ fpathWrite = fp </> "tmp_write.txt"
139+ writeFile fpathRead testDataLarge
140+ writeFile fpathWrite ""
141+ hr <- openFile fpathRead ReadMode
142+ hw <- openFile fpathWrite ReadWriteMode
143+ hSeek hw AbsoluteSeek 0
144+ S.fold (FH.writeChunks hw)
145+ $ S.unfold FH.readChunksWithBufferOf (1024, hr)
146+ hFlush hw
147+ hSeek hw AbsoluteSeek 0
148+ ls <- S.toList $ S.unfold FH.read hw
149+ let arr = A.fromList ls
150+ return (testDataLarge == utf8ToString arr)
151+
152+main :: IO ()
153+main =
154+ hspec $
155+ H.parallel $
156+ modifyMaxSuccess (const maxTestCount) $ do
157+ describe "Read" $ do
158+ prop "testRead" $ testRead readFromHandle
159+ prop "testReadWithBuffer" $ testRead readWithBufferFromHandle
160+ prop "testReadChunks" $ testRead readChunksFromHandle
161+ prop "testReadChunksWithBuffer" $ testRead readChunksWithBuffer
162+ describe "Write" $ do
163+ prop "testWrite" $ testWrite FH.write
164+ prop "testWriteWithBufferOf" $ testWrite $ FH.writeWithBufferOf 1024
165+ prop "testWriteWithChunk" $ testWriteWithChunk
test/streamly-tests.cabal+6
@@ -330,3 +330,9 @@ test-suite FileSystem.Event
330 main-is: Streamly/Test/FileSystem/Event.hs
330 main-is: Streamly/Test/FileSystem/Event.hs
331 if os(darwin)
331 if os(darwin)
332 buildable: False
332 buildable: False
333+
334+test-suite FileSystem.Handle
335+ import: test-options
336+ type: exitcode-stdio-1.0
337+ main-is: Streamly/Test/FileSystem/Handle.hs
338+ ghc-options: -main-is Streamly.Test.FileSystem.Handle.main