on master help sign in
Add test cases for Data.Fold/Array, FileSystem.Handle

Added the following test cases to Data.Fold: drain, drainBy, mean, stdDev, variance, mconcat, foldMap, foldMapM, lookup, mapM, teeWithLength, teeWithMax, distribute, partition, unzip Added the following test cases to Data.Array.Storable.Foreign * fromList and fromListN Added FileSystem.Handle module

commit0bd1586175b90e02a754ecf0fe3f7c0bddc19c12 authorRanjeet Ranjan <ranjeetr@composewell.com> dateMon 30 Nov 2020, 21:40 (6 years ago) parent2e0a445
5 files changed+465−74
streamly.cabal+1 test/Streamly/Test/Common/Array.hs+22−2 test/Streamly/Test/Data/Fold.hs+271−72 test/Streamly/Test/FileSystem/Handle.hs+165 test/streamly-tests.cabal+6
streamly.cabal+1
@@ -168,6 +168,7 @@ extra-source-files:
168168 test/Streamly/Test/Data/Array/Storable/Foreign.hs
169169 test/Streamly/Test/Data/Parser/ParserD.hs
170170 test/Streamly/Test/FileSystem/Event.hs
171+ test/Streamly/Test/FileSystem/Handle.hs
171172 test/Streamly/Test/Prelude.hs
172173 test/Streamly/Test/Prelude/*.hs
173174 test/Streamly/Test/Unicode/Stream.hs
test/Streamly/Test/Common/Array.hs+22−2
@@ -16,12 +16,10 @@ import Foreign.Storable (Storable(..))
1616import Test.Hspec.QuickCheck
1717import Test.QuickCheck (Property, forAll, Gen, vectorOf, arbitrary, choose)
1818import Test.QuickCheck.Monadic (monadicIO, assert, run)
19−
2019import Test.Hspec as H
2120
2221import Streamly.Prelude (SerialT)
2322import qualified Streamly.Prelude as S
24−
2523#ifdef TEST_SMALL_ARRAY
2624import qualified Streamly.Internal.Data.SmallArray as A
2725type Array = A.SmallArray
@@ -125,12 +123,31 @@ testFromStreamNUnfold = genericTestFromTo A.fromStreamN (S.unfold A.read) (==)
125123testFromStreamNToStream :: Property
126124testFromStreamNToStream = genericTestFromTo A.fromStreamN A.toStream (==)
127125
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+
128136#ifndef TEST_SMALL_ARRAY
129137testFromStreamToStream :: Property
130138testFromStreamToStream = genericTestFromTo (const A.fromStream) A.toStream (==)
131139
132140testFoldUnfold :: Property
133141testFoldUnfold = 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)
134151#endif
135152
136153#if defined(TEST_ARRAY) ||\
@@ -202,10 +219,13 @@ main =
202219 prop "toStreamRev . writeN === reverse" testFoldNToStreamRev
203220 prop "read . fromStreamN === id" testFromStreamNUnfold
204221 prop "toStream . fromStreamN === id" testFromStreamNToStream
222+ prop "First N elements of a list" testFromListN
223+
205224#ifndef TEST_SMALL_ARRAY
206225 prop "length . fromStream === n" testLengthFromStream
207226 prop "toStream . fromStream === id" testFromStreamToStream
208227 prop "read . write === id" testFoldUnfold
228+ prop "From a list" testFromList
209229#endif
210230
211231#if defined(TEST_ARRAY) ||\
test/Streamly/Test/Data/Fold.hs+271−72
@@ -1,16 +1,31 @@
11module Main (main) where
22
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)
320import 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+
523import qualified Streamly.Internal.Data.Fold as F
624import qualified Streamly.Prelude as S
725import qualified Streamly.Data.Fold as FL
826
927import Test.Hspec as H
1028import Test.Hspec.QuickCheck
11−import Test.QuickCheck
12− (Property, property, forAll, Gen, vectorOf, arbitrary, choose, listOf)
13−import Test.QuickCheck.Monadic (monadicIO, assert, run)
1429
1530maxStreamLen :: Int
1631maxStreamLen = 1000
@@ -43,75 +58,75 @@ rollingHashFirstN =
4358 b <- run $ S.fold (F.rollingHashFirstN n) $ S.fromList vec
4459 assert $ a == b
4560
46−
4761head :: [Int] -> Expectation
4862head ls = S.fold FL.head (S.fromList ls) `shouldReturn` headl ls
49− where
50− headl [] = Nothing
51− headl (x:_) = Just x
5263
64+ where
65+
66+ headl [] = Nothing
67+ headl (x:_) = Just x
5368
5469length :: [Int] -> Expectation
5570length ls = S.fold FL.length (S.fromList ls) `shouldReturn` Prelude.length ls
5671
57−
5872sum :: [Int] -> Expectation
5973sum ls = S.fold FL.sum (S.fromList ls) `shouldReturn` foldl (+) 0 ls
6074
61−
6275product :: [Int] -> Expectation
6376product ls = S.fold FL.product (S.fromList ls) `shouldReturn` foldl (*) 1 ls
6477
65−
6678lesser :: (a -> a -> Ordering) -> a -> a -> a
6779lesser f x y = if f x y == LT then x else y
6880
69−
7081greater :: (a -> a -> Ordering) -> a -> a -> a
7182greater f x y = if f x y == GT then x else y
7283
73−
7484foldMaybe :: (b -> a -> b) -> b -> [a] -> Maybe b
7585foldMaybe f acc ls =
76− case ls of
77− [] -> Nothing
78− _ -> Just (foldl f acc ls)
79−
86+ case ls of
87+ [] -> Nothing
88+ _ -> Just (foldl f acc ls)
8089
8190maximumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
82−maximumBy genmin f ls = S.fold (FL.maximumBy f) (S.fromList ls) `shouldReturn` foldMaybe (greater f) genmin ls
83−
91+maximumBy genmin f ls =
92+ S.fold (FL.maximumBy f) (S.fromList ls)
93+ `shouldReturn`
94+ foldMaybe (greater f) genmin ls
8495
8596maximum :: (Show a, Ord a) => a -> [a] -> Expectation
86−maximum genmin ls = S.fold FL.maximum (S.fromList ls) `shouldReturn` foldMaybe (greater compare) genmin ls
87−
97+maximum genmin ls =
98+ S.fold FL.maximum (S.fromList ls)
99+ `shouldReturn` foldMaybe (greater compare) genmin ls
88100
89101minimumBy :: (Ord a, Show a) => a -> (a -> a -> Ordering) -> [a] -> Expectation
90−minimumBy genmax f ls = S.fold (FL.minimumBy f) (S.fromList ls) `shouldReturn` foldMaybe (lesser f) genmax ls
91−
102+minimumBy genmax f ls =
103+ S.fold (FL.minimumBy f) (S.fromList ls)
104+ `shouldReturn` foldMaybe (lesser f) genmax ls
92105
93106minimum :: (Show a, Ord a) => a -> [a] -> Expectation
94−minimum genmax ls = S.fold FL.minimum (S.fromList ls) `shouldReturn` foldMaybe (lesser compare) genmax ls
95−
107+minimum genmax ls =
108+ S.fold FL.minimum (S.fromList ls)
109+ `shouldReturn` foldMaybe (lesser compare) genmax ls
96110
97111toList :: [Int] -> Expectation
98112toList ls = S.fold FL.toList (S.fromList ls) `shouldReturn` ls
99113
100−
101114safeLast :: [a] -> Maybe a
102115safeLast [] = Nothing
103116safeLast (x:[]) = Just x
104117safeLast (_:xs) = safeLast xs
105118
106−
107119last :: [String] -> Expectation
108120last ls = S.fold FL.last (S.fromList ls) `shouldReturn` safeLast ls
109121
110122mapMaybe :: [Int] -> Expectation
111123mapMaybe ls =
112− (let f = F.mapMaybe (\x -> if even x then Just x else Nothing) FL.toList
113− in S.fold f (S.fromList ls))
114− `shouldReturn` filter even ls
124+ let maybeEven x =
125+ if even x
126+ then Just x
127+ else Nothing
128+ f = F.mapMaybe maybeEven FL.toList
129+ in S.fold f (S.fromList ls) `shouldReturn` filter even ls
115130
116131nth :: Int -> [a] -> Maybe a
117132nth idx (x:xs) = if idx == 0
@@ -121,48 +136,52 @@ nth idx (x:xs) = if idx == 0
121136 else nth (idx - 1) xs
122137nth _ [] = Nothing
123138
124−
125139index :: Int -> [String] -> Expectation
126140index idx ls = let x = S.fold (FL.index idx) (S.fromList ls)
127141 in x `shouldReturn` (nth idx ls)
128142
129−
130143find :: (Show a, Eq a) => (a -> Bool) -> [a] -> Expectation
131144find f ls = do
132− let x = S.fold (FL.findIndex f) (S.fromList ls)
133− y <- x
134− case y of
135− Nothing -> S.fold (FL.find f) (S.fromList ls) `shouldReturn` Nothing
136− Just idx -> S.fold (FL.any f) (S.fromList $ take idx ls) `shouldReturn` False
137−
145+ y <- S.fold (FL.findIndex f) (S.fromList ls)
146+ case y of
147+ Nothing ->
148+ let fld = S.fold (FL.find f) (S.fromList ls)
149+ in fld `shouldReturn` Nothing
150+ Just idx ->
151+ let fld = S.fold (FL.any f) (S.fromList $ take idx ls)
152+ in fld `shouldReturn` False
138153
139154neg :: (a -> Bool) -> a -> Bool
140155neg f x = if f x == True then False else True
141156
142157findIndex :: (a -> Bool) -> [a] -> Expectation
143158findIndex f ls = do
144− let x = S.fold (FL.findIndex f) (S.fromList ls)
145− y <- x
146− case y of
147− Nothing -> S.fold (FL.all $ neg f) (S.fromList ls) `shouldReturn` True
148− Just idx -> if idx == 0
149− then S.fold (FL.all f) (S.fromList []) `shouldReturn` True
150− else S.fold (FL.all f) (S.fromList $ (take idx ls)) `shouldReturn` False
159+ y <- S.fold (FL.findIndex f) (S.fromList ls)
160+ case y of
161+ Nothing ->
162+ let fld = S.fold (FL.all $ neg f) (S.fromList ls)
163+ in fld `shouldReturn` True
164+ Just idx -> if idx == 0
165+ then
166+ S.fold (FL.all f) (S.fromList []) `shouldReturn` True
167+ else
168+ S.fold (FL.all f) (S.fromList $ (take idx ls))
169+ `shouldReturn`
170+ False
151171
152172predicate :: Int -> Bool
153173predicate x = if x * x < 100 then True else False
154174
155175elemIndex :: Int -> [Int] -> Expectation
156176elemIndex elm ls = do
157− let x = S.fold (FL.elemIndex elm) (S.fromList ls)
158− y <- x
159− case y of
160− Nothing -> S.fold (FL.any (\z -> if z == elm
161− then True
162− else False)) (S.fromList ls) `shouldReturn` False
163− Just idx -> S.fold (FL.any (\z -> if z == elm
164− then True
165− else False)) (S.fromList $ (take idx ls)) `shouldReturn` False
177+ y <- S.fold (FL.elemIndex elm) (S.fromList ls)
178+ case y of
179+ Nothing ->
180+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
181+ in fld `shouldReturn` False
182+ Just idx ->
183+ let fld = S.fold (FL.any (== elm)) (S.fromList $ (take idx ls))
184+ in fld `shouldReturn` False
166185
167186null :: [Int] -> Expectation
168187null 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
170189 _ -> False
171190elem :: Int -> [Int] -> Expectation
172191elem elm ls = do
173− let x = S.fold (FL.elem elm) (S.fromList ls)
174− y <- x
175− S.fold (FL.any (\z -> if z == elm
176− then True
177− else False)) (S.fromList ls) `shouldReturn` y
178−
192+ y <- S.fold (FL.elem elm) (S.fromList ls)
193+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
194+ fld `shouldReturn` y
179195
180196notElem :: Int -> [Int] -> Expectation
181197notElem elm ls = do
182− let x = S.fold (FL.notElem elm) (S.fromList ls)
183− y <- x
184− S.fold (FL.any (\z -> if z == elm
185− then True
186− else False)) (S.fromList ls) `shouldReturn` (if y == True
187− then False
188− else True)
189−
198+ y <- S.fold (FL.notElem elm) (S.fromList ls)
199+ let fld = S.fold (FL.any (== elm)) (S.fromList ls)
200+ fld `shouldReturn` not y
190201
191202all :: (a -> Bool) -> [a] -> Expectation
192−all f ls = S.fold (FL.all f) (S.fromList ls) `shouldReturn` Prelude.and (map f ls)
193−
203+all f ls =
204+ S.fold (FL.all f) (S.fromList ls) `shouldReturn` Prelude.and (map f ls)
194205
195206any :: (a -> Bool) -> [a] -> Expectation
196207any f ls = S.fold (FL.any f) (S.fromList ls) `shouldReturn` Prelude.any f ls
197208
198−
199209and :: [Bool] -> Expectation
200210and ls = S.fold FL.and (S.fromList ls) `shouldReturn` Prelude.and ls
201211
202−
203212or :: [Bool] -> Expectation
204213or ls = S.fold FL.or (S.fromList ls) `shouldReturn` Prelude.or ls
205214
@@ -230,6 +239,181 @@ sliceSepByMax =
230239 Right xs -> checkListEqual xs ys
231240 Left _ -> property False
232241
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+
233417main :: IO ()
234418main = hspec $
235419 describe "Fold s" $ do
@@ -259,3 +443,18 @@ main = hspec $
259443 prop "ltake" ltake
260444 prop "sliceSepBy" sliceSepBy
261445 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
330330 main-is: Streamly/Test/FileSystem/Event.hs
331331 if os(darwin)
332332 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