on 0bd3d4e help sign in
228 lines 7.8 kB history raw
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
9module Streamly.Test.FileSystem.Handle (main) where
11import Data.Functor.Identity (runIdentity)
12import Data.Word (Word8)
13import Streamly.Internal.Data.Stream (Stream)
14import Streamly.Internal.System.IO (defaultChunkSize)
15import System.FilePath ((</>))
16import System.IO
17 ( Handle
18 , IOMode(..)
19 , SeekMode(..)
20 , hClose
21 , hFlush
22 , hSeek
23 , openFile
24 )
25import System.IO.Temp (withSystemTempDirectory)
26import Test.QuickCheck (Property, forAll, Gen, vectorOf, choose)
27import Test.QuickCheck.Monadic (monadicIO, assert, run)
29import qualified Streamly.Data.Fold as Fold
30import qualified Streamly.Internal.FileSystem.Handle as Handle
31import qualified Streamly.Internal.Data.Stream as Stream
32import qualified Streamly.Internal.Data.Array as Array
33import qualified Streamly.Internal.Unicode.Stream as Unicode
35import Test.Hspec as H
36import Test.Hspec.QuickCheck
38maxArrLen :: Int
39maxArrLen = defaultChunkSize * 8
41maxTestCount :: Int
42maxTestCount = 10
44chooseWord8 :: (Word8, Word8) -> Gen Word8
45chooseWord8 = choose
47toList :: Monad m => Stream m a -> m [a]
48toList = Stream.fold Fold.toList
50utf8ToString :: Array.Array Word8 -> String
51utf8ToString =
52 runIdentity . toList . Unicode.decodeUtf8' . Array.read
54testData :: String
55testData = "This is the test data for FileSystem.Handle ??`!@#$%^&*~~))`]"
57testDataLarge :: String
58testDataLarge = concat $ replicate 6000 testData
60testBinData :: String
61testBinData = "01234567890123456789012345678901234567890123456789"
63executor :: (Handle -> Stream IO Char) -> IO (Stream IO Char)
64executor 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
71readFromHandle :: IO (Stream IO Char)
72readFromHandle =
73 let f = Unicode.decodeUtf8 . Stream.unfold Handle.reader
74 in executor f
76readWithBufferFromHandle :: IO (Stream IO Char)
77readWithBufferFromHandle =
78 let f1 = (\h -> (1024, h))
79 f2 = Unicode.decodeUtf8 . Stream.unfold Handle.readerWith . f1
80 in executor f2
82readChunksFromHandle :: IO (Stream IO Char)
83readChunksFromHandle =
84 let f = Unicode.decodeUtf8
85 . Stream.concatMap Array.read
86 . Stream.unfold Handle.chunkReader
87 in executor f
89readChunksWithBuffer :: IO (Stream IO Char)
90readChunksWithBuffer =
91 let f1 = (\h -> (1024, h))
92 f2 =
93 Unicode.decodeUtf8
94 . Stream.concatMap Array.read
95 . Stream.unfold Handle.chunkReaderWith
96 . f1
97 in executor f2
99testRead :: IO (Stream IO Char) -> Property
100testRead fn = monadicIO $ do
101 let v2 = Stream.fromList testDataLarge
102 v1 <- run fn
103 res <- run $ Stream.eqBy (==) v1 v2
104 assert res
106testWrite :: (Handle -> Fold.Fold IO Word8 ()) -> Property
107testWrite hfold =
108 forAll (choose (0, maxArrLen)) $ \len ->
109 forAll (vectorOf len $ chooseWord8 (0, 255)) $ \list0 ->
110 monadicIO $ do
111 res <- run $ go list0
112 assert res
114 where
116 go list =
117 withSystemTempDirectory "fs_handle" $ \fp -> do
118 let fpathWrite = fp </> "tmp_write.txt"
119 writeFile fpathWrite ""
120 h <- openFile fpathWrite ReadWriteMode
121 hSeek h AbsoluteSeek 0
122 _ <- Stream.fold (hfold h) $ Stream.fromList list
123 hFlush h
124 hSeek h AbsoluteSeek 0
125 ls <- toList $ Stream.unfold Handle.reader h
126 hClose h
127 return (ls == list)
129testWriteWithChunk :: Property
130testWriteWithChunk =
131 monadicIO $ do
132 res <- run go
133 assert res
135 where
137 go =
138 withSystemTempDirectory "fs_handle" $ \fp -> do
139 let fpathRead = fp </> "tmp_read.txt"
140 fpathWrite = fp </> "tmp_write.txt"
141 writeFile fpathRead testDataLarge
142 writeFile fpathWrite ""
143 hr <- openFile fpathRead ReadMode
144 hw <- openFile fpathWrite ReadWriteMode
145 hSeek hw AbsoluteSeek 0
146 _ <- Stream.fold (Handle.writeChunks hw)
147 $ Stream.unfold Handle.chunkReaderWith (1024, hr)
148 hFlush hw
149 hSeek hw AbsoluteSeek 0
150 ls <- toList $ Stream.unfold Handle.reader hw
151 let arr = Array.fromList ls
152 return (testDataLarge == utf8ToString arr)
154testReadChunksFromToWith :: Int -> Int -> Int -> [[Word8]] -> Property
155testReadChunksFromToWith from to buffSize res = monadicIO $ run go
157 where
159 go =
160 withSystemTempDirectory "fs_handle" $ \fp -> do
161 let fpathRead = fp </> "tmp_read.txt"
162 writeFile fpathRead testBinData
163 h <- openFile fpathRead ReadMode
164 ls <-
165 toList
166 $ Stream.unfold
167 Handle.chunkReaderFromToWith (from, to, buffSize, h)
168 return (res `shouldBe` fmap Array.toList ls)
170-- Test for first byte
171testReadChunksFromToWithFirstByte :: Property
172testReadChunksFromToWithFirstByte = testReadChunksFromToWith 0 0 15 [[48]]
174-- Test for second byte
175testReadChunksFromToWithSecondByte :: Property
176testReadChunksFromToWithSecondByte = testReadChunksFromToWith 1 1 15 [[49]]
178-- Test for second to 10th bytes
179testReadChunksFromToWithSecondToTenthBytes :: Property
180testReadChunksFromToWithSecondToTenthBytes =
181 testReadChunksFromToWith 1 10 15 [[49,50,51,52,53,54,55,56,57,48]]
183-- Test for offset of buffer size
184testReadChunksFromToWithBuffSizeOffset :: Property
185testReadChunksFromToWithBuffSizeOffset =
186 testReadChunksFromToWith 15 25 15 [[53,54,55,56,57,48,49,50,51,52,53]]
188-- Test with multi buffer size
189testReadChunksFromToWithMultiBuff :: Property
190testReadChunksFromToWithMultiBuff =
191 testReadChunksFromToWith
192 5 22 5 [[53,54,55,56,57],[48,49,50,51,52],[53,54,55,56,57],[48,49,50]]
194testReadChunksFromToWithRangeInvalid :: Property
195testReadChunksFromToWithRangeInvalid = testReadChunksFromToWith 15 5 15 []
197moduleName :: String
198moduleName = "FileSystem.Handle"
200main :: IO ()
201main =
202 hspec $
203 H.parallel $
204 modifyMaxSuccess (const maxTestCount) $ do
205 describe moduleName $ do
206 describe "Read From Handle" $ do
207 prop "read" $ testRead readFromHandle
208 prop "readWith" $ testRead readWithBufferFromHandle
209 prop "readChunks" $ testRead readChunksFromHandle
210 prop "readChunksWith" $ testRead readChunksWithBuffer
211 prop "readChunksFromToWith (0,0,n)"
212 testReadChunksFromToWithFirstByte
213 prop "readChunksFromToWith (1,1,n)"
214 testReadChunksFromToWithSecondByte
215 prop "readChunksFromToWith (1,10,n)"
216 testReadChunksFromToWithSecondToTenthBytes
217 prop "readChunksFromToWith (n,<2n,n)"
218 testReadChunksFromToWithBuffSizeOffset
219 prop "readChunksFromToWith (n,>2n,n)"
220 testReadChunksFromToWithMultiBuff
221 prop "readChunksFromToWith (n,<n,n)"
222 testReadChunksFromToWithRangeInvalid
223 describe "Write To Handle" $ do
224 prop "write" $ testWrite Handle.write
225 prop "writeWith"
226 $ testWrite $ Handle.writeWith 1024
227 -- XXX This test needs a lot of stack when built with -O0
228 prop "writeChunks" testWriteWithChunk