on master help sign in
Implement common FS events

Fix mac compile issue Redue lower version of filepath Fix review comments Fix Mac compile issue Fix review comments

commit3b8932e74c8864ab9e1775a113b830f3a1a2ab98 authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateWed 11 Aug 2021, 12:00 (5 years ago) parent85b328a
6 files changed+191−124
src/Streamly/Internal/FileSystem/Event.hs+102 src/Streamly/Internal/FileSystem/Event/Darwin.hs+11−9 src/Streamly/Internal/FileSystem/Event/Linux.hs+25−37 src/Streamly/Internal/FileSystem/Event/Windows.hs+38−63 streamly.cabal+2 test/Streamly/Test/FileSystem/Event.hs+13−15
src/Streamly/Internal/FileSystem/Event.hsnew file+102
@@ -0,0 +1,102 @@
1+-- |
2+-- Module : Streamly.FileSystem.Event
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+
10+module Streamly.Internal.FileSystem.Event
11+ ( Config
12+ , Event
13+ , Toggle
14+ , defaultConfig
15+ , setAllEvents
16+
17+ -- ** Events of Interest
18+ , setRootMoved
19+
20+ -- * Handling Events
21+ , getAbsolutePath
22+
23+ -- ** Item CRUD events
24+ , isCreated
25+ , isDeleted
26+ , isMoved
27+ , isModified
28+
29+ -- ** Exception Conditions
30+ , isOverflow
31+
32+ -- * Debugging
33+ , showEvent
34+ )
35+where
36+
37+import Data.Word (Word8)
38+import Streamly.Internal.Data.Array.Foreign (Array)
39+
40+#if defined(CABAL_OS_DARWIN)
41+import qualified Streamly.Internal.FileSystem.Event.Darwin as Event
42+#elif defined(CABAL_OS_LINUX)
43+import qualified Streamly.Internal.FileSystem.Event.Linux as Event
44+#elif defined(CABAL_OS_WINDOWS)
45+import qualified Streamly.Internal.FileSystem.Event.Windows as Event
46+#else
47+#error "FS Events not supported on this platform"
48+#endif
49+
50+-------------------------------------------------------------------------------
51+-- Main
52+-------------------------------------------------------------------------------
53+
54+type Toggle = Event.Toggle
55+type Config = Event.Config
56+type Event = Event.Event
57+
58+setRootMoved :: Toggle -> Config -> Config
59+setRootMoved = Event.setRootMoved
60+
61+setAllEvents :: Toggle -> Config -> Config
62+#if defined(CABAL_OS_DARWIN)
63+setAllEvents s cfg = defaultConfig
64+#else
65+setAllEvents = Event.setAllEvents
66+#endif
67+
68+getAbsolutePath :: Event -> Array Word8
69+getAbsolutePath = Event.getAbsPath
70+
71+isCreated :: Event -> Bool
72+isCreated = Event.isCreated
73+
74+isDeleted :: Event -> Bool
75+isDeleted = Event.isDeleted
76+
77+isMoved :: Event -> Bool
78+#if defined(CABAL_OS_DARWIN)
79+isMoved = Event.isMoved
80+#else
81+isMoved ev = Event.isMovedFrom ev || Event.isMovedTo ev
82+#endif
83+
84+isModified :: Event -> Bool
85+isModified = Event.isModified
86+
87+isOverflow :: Event -> Bool
88+#if defined(CABAL_OS_DARWIN)
89+isOverflow = Event.isMustScanSubdirs
90+#else
91+isOverflow = Event.isOverflow
92+#endif
93+
94+showEvent :: Event -> String
95+showEvent = Event.showEvent
96+
97+defaultConfig :: Config
98+#if defined(CABAL_OS_DARWIN)
99+defaultConfig = Event.Config 0.0 0
100+#else
101+defaultConfig = Event.Config True 0
102+#endif
src/Streamly/Internal/FileSystem/Event/Darwin.hs+11−9
@@ -81,7 +81,7 @@ module Streamly.Internal.FileSystem.Event.Darwin
8181 -- * Creating a Watch
8282
8383 -- ** Default configuration
84− Config
84+ Config (..)
8585 , Toggle (..)
8686 , defaultConfig
8787
@@ -90,7 +90,7 @@ module Streamly.Internal.FileSystem.Event.Darwin
9090 , setEventBatching
9191
9292 -- ** Events of Interest
93− , setRootChanged
93+ , setRootMoved
9494 , setFileEvents
9595 , setIgnoreSelf
9696#if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGFULLHISTORY
@@ -214,7 +214,7 @@ data BatchInfo =
214214-- /Pre-release/
215215--
216216setEventBatching :: BatchInfo -> Config -> Config
217−setEventBatching batchInfo cfg@Config{..} =
217+setEventBatching batchInfo cfg =
218218 let (t, status) =
219219 case batchInfo of
220220 Throttle sec | sec < 0 -> (0, On)
@@ -255,8 +255,8 @@ foreign import ccall safe
255255--
256256-- /Pre-release/
257257--
258−setRootChanged :: Toggle -> Config -> Config
259−setRootChanged = setFlag kFSEventStreamCreateFlagWatchRoot
258+setRootMoved :: Toggle -> Config -> Config
259+setRootMoved = setFlag kFSEventStreamCreateFlagWatchRoot
260260
261261foreign import ccall safe
262262 "FSEventStreamCreateFlagFileEvents"
@@ -321,7 +321,7 @@ setFullHistory = setFlag kFSEventStreamCreateFlagFullHistory
321321--
322322-- * 'setEventBatching' ('Batch' 0.0)
323323-- * 'setFileEvents' 'On'
324−-- * 'setRootChanged' 'Off'
324+-- * 'setRootMoved' 'Off'
325325-- * 'setIgnoreSelf' 'Off'
326326#if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGFULLHISTORY
327327-- * 'setFullHistory' 'Off'
@@ -498,14 +498,16 @@ watchToStream (Watch handle _ _) =
498498--
499499-- BUGS: If a watch is started on a non-existing path then the path is not
500500-- watched even if it is created later. The macOS API does not fail for a
501−-- non-existing path. If a non-existing path is watched with 'setRootChanged'
501+-- non-existing path. If a non-existing path is watched with 'setRootMoved'
502502-- then an 'isRootChanged' event is reported if the path is created later and
503503-- the "path" field in the event is set to the dirname of the path rather than
504504-- the full absolute path. This is the observed behavior on macOS 10.15.1.
505505--
506506-- @
507507-- {-\# LANGUAGE MagicHash #-}
508−-- watchTreesWith ('setIgnoreSelf' 'On' . 'setRootChanged' 'On') [Array.fromCString\# "path"#]
508+-- watchTreesWith
509+-- ('setIgnoreSelf' 'On' . 'setRootMoved' 'On')
510+-- [Array.fromCString\# "path"#]
509511-- @
510512--
511513-- /Pre-release/
@@ -656,7 +658,7 @@ foreign import ccall safe
656658-- object itself. Note that the object may become unreachable or deleted after
657659-- a change of path.
658660--
659−-- /Applicable only when 'setRootChanged' is 'On'/
661+-- /Applicable only when 'setRootMoved' is 'On'/
660662--
661663-- /Occurs only for the watched path/
662664--
src/Streamly/Internal/FileSystem/Event/Linux.hs+25−37
@@ -3,7 +3,7 @@
33-- Copyright : (c) 2020 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
6−-- Stability : pre-release
6+-- Stability : experimental
77-- Portability : GHC
88--
99-- =Overview
@@ -63,7 +63,7 @@ module Streamly.Internal.FileSystem.Event.Linux
6363 -- * Subscribing to events
6464
6565 -- ** Default configuration
66− Config
66+ Config (..)
6767 , Toggle (..)
6868 , defaultConfig
6969
@@ -101,8 +101,7 @@ module Streamly.Internal.FileSystem.Event.Linux
101101 -- ** Watch APIs
102102 -- XXX watchPaths is redundant now because we can use watchTrees with
103103 -- setRecursiveMode False. Perhaps we can use a common "watch" API.
104− , watchPathsWith
105− , watchPaths
104+ , watch
106105 , watchTreesWith
107106 , watchTrees
108107 , addToWatch
@@ -157,7 +156,7 @@ import Data.Functor.Identity (runIdentity)
157156import Data.IntMap.Lazy (IntMap)
158157import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)
159158import Data.List.NonEmpty (NonEmpty)
160−#if !(MIN_VERSION_base(4,11,0))
159+#if !(MIN_VERSION_base(4,13,0))
161160import Data.Semigroup (Semigroup(..))
162161#endif
163162import Data.Word (Word8, Word32)
@@ -172,6 +171,7 @@ import GHC.IO.Handle.FD (mkHandleFromFD)
172171import Streamly.Prelude (SerialT)
173172import Streamly.Internal.Data.Parser (Parser)
174173import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
174+--import System.FilePath ((</>))
175175import System.IO (Handle, hClose, IOMode(ReadMode))
176176#if !MIN_VERSION_base(4,10,0)
177177import Control.Concurrent.MVar (readMVar)
@@ -637,7 +637,7 @@ ensureTrailingSlash path =
637637-- /Pre-release/
638638--
639639addToWatch :: Config -> Watch -> Array Word8 -> Array Word8 -> IO ()
640−addToWatch cfg@Config{..} watch@(Watch handle wdMap) root0 path0 = do
640+addToWatch cfg@Config{..} watch0@(Watch handle wdMap) root0 path0 = do
641641 -- XXX do not add if the path is already added
642642 -- XXX if the watch is added by the scan and not via an event we can
643643 -- generate a create event assuming that the create may have been lost. We
@@ -686,7 +686,7 @@ addToWatch cfg@Config{..} watch@(Watch handle wdMap) root0 path0 = do
686686 -- XXX toDirs currently uses paths as String, we need to convert it
687687 -- to "/" separated by byte arrays.
688688 when watchRec $ do
689− S.mapM_ (\p -> addToWatch cfg watch root (path <> p))
689+ S.mapM_ (\p -> addToWatch cfg watch0 root (path <> p))
690690 $ S.mapM toUtf8
691691 $ Dir.toDirs $ utf8ToString absPath
692692
@@ -843,35 +843,30 @@ watchToStream cfg wt@(Watch handle _) = do
843843 S.parseMany (readOneEvent cfg wt) $ S.unfold FH.read handle
844844
845845-- | Start monitoring a list of file system paths for file system events with
846−-- the supplied configuration operation over the 'defaultConfig'. The
847−-- paths could be files or directories. When the path is a directory, only the
846+-- the supplied recursive mode and configuration. The paths could be files or
847+-- directories. When recursive mode is True and the path is a directory, the
848+-- whole directory tree under it is watched recursively.
849+-- When recursive mode is False and the path is a directory, only the
848850-- files and directories directly under the watched directory are monitored,
849851-- contents of subdirectories are not monitored. Monitoring starts from the
850−-- current time onwards. The paths are specified as "/" separated 'Array' of
852+-- current time onwards. The paths are specified as UTF-8 encoded 'Array' of
851853-- 'Word8'.
852854--
853855-- @
854−-- watchPathsWith
855−-- ('setFollowSymLinks' On . 'setUnwatchMoved' Off)
856−-- [Array.fromCString\# "dir"#]
857−-- @
858−--
859−-- /Pre-release/
860−--
861−watchPathsWith ::
862− (Config -> Config) -> NonEmpty (Array Word8) -> SerialT IO Event
863−watchPathsWith f = watchTreesWith (f . setRecursiveMode False)
864−
865−-- | Like 'watchPathsWith' but uses the 'defaultConfig' options.
866−--
867−-- @
868−-- watchPaths = watchPathsWith id
856+-- watch
857+-- True
858+-- ('setFollowSymLinks' On . 'setUnwatchMoved' Off)
859+-- defaultConfig
860+-- [Array.fromCString\# "dir"#]
869861-- @
870862--
871863-- /Pre-release/
872864--
873−watchPaths :: NonEmpty (Array Word8) -> SerialT IO Event
874−watchPaths = watchPathsWith id
865+watch :: Bool -> Config -> NonEmpty (Array Word8) -> SerialT IO Event
866+watch rec cfg paths =
867+ case rec of
868+ True -> watchTreesWith (\_ -> cfg) paths
869+ False -> watchTreesWith (\_ -> setRecursiveMode False cfg) paths
875870
876871-- XXX We should not go across the mount points of network file systems or file
877872-- systems that are known to not generate any events.
@@ -951,14 +946,8 @@ getRoot Event{..} =
951946getRelPath :: Event -> Array Word8
952947getRelPath Event{..} = eventRelPath
953948
954−
955−-- | Get the absolute file system object path for which the event is generated.
956−-- The path is a "/" separated array of bytes.
957−--
958−-- /Pre-release/
959−--
960949getAbsPath :: Event -> Array Word8
961−getAbsPath ev = getRoot ev <> getRelPath ev
950+getAbsPath ev = getRoot ev <> A.fromCString# "/"# <> getRelPath ev
962951
963952-- XXX should we use a Maybe?
964953-- | Cookie is set when a rename occurs. The cookie value can be used to
@@ -1199,8 +1188,7 @@ showEvent ev@Event{..} =
11991188 "--------------------------"
12001189 ++ "\nWd = " ++ show eventWd
12011190 ++ "\nRoot = " ++ show (utf8ToString $ getRoot ev)
1202− ++ "\nRelative Path = " ++ show (utf8ToString $ getRelPath ev)
1203− ++ "\nAbsolute Path = " ++ show (utf8ToString $ getAbsPath ev)
1191+ ++ "\nPath = " ++ show (utf8ToString $ getRelPath ev)
12041192 ++ "\nCookie = " ++ show (getCookie ev)
12051193 ++ "\nFlags " ++ show eventFlags
12061194
@@ -1231,4 +1219,4 @@ showEvent ev@Event{..} =
12311219#else
12321220#warning "Disabling module Streamly.Internal.FileSystem.Event.Linux. Does not support kernels older than 2.6.36."
12331221module Streamly.Internal.FileSystem.Event.Linux () where
1234−#endif
1222+#endif
src/Streamly/Internal/FileSystem/Event/Windows.hs+38−63
@@ -47,7 +47,7 @@ module Streamly.Internal.FileSystem.Event.Windows
4747 -- * Subscribing to events
4848
4949 -- ** Default configuration
50− Config
50+ Config (..)
5151 , Event (..)
5252 , Toggle (..)
5353 , setFlag
@@ -61,22 +61,21 @@ module Streamly.Internal.FileSystem.Event.Windows
6161 -- ** Events of Interest
6262 -- *** Root Level Events
6363 , setModifiedFileName
64− , setModifiedDirName
64+ , setRootMoved
6565 , setModifiedAttribute
6666 , setModifiedSize
6767 , setModifiedLastWrite
6868 , setModifiedSecurity
6969
7070 -- ** Watch APIs
71− , watchPaths
72− , watchPathsWith
71+ , watch
7372 , watchTrees
7473 , watchTreesWith
7574
7675 -- * Handling Events
77− , getAbsPath
7876 , getRelPath
7977 , getRoot
78+ , getAbsPath
8079
8180 -- ** Item CRUD events
8281 , isCreated
@@ -124,6 +123,7 @@ import System.Win32.Types (BOOL, DWORD, HANDLE, LPVOID, LPDWORD, failIfFalse_)
124123import qualified Data.List.NonEmpty as NonEmpty
125124import qualified Streamly.Internal.Data.Stream.IsStream as S
126125import qualified Streamly.Internal.Unicode.Stream as U
126+import qualified Streamly.Internal.Unicode.Utf8 as UTF8
127127import qualified Streamly.Internal.Data.Array.Foreign as A
128128import Streamly.Internal.Data.Array.Foreign (Array)
129129
@@ -179,8 +179,8 @@ setModifiedFileName = setFlag fILE_NOTIFY_CHANGE_FILE_NAME
179179--
180180-- /Pre-release/
181181--
182−setModifiedDirName :: Toggle -> Config -> Config
183−setModifiedDirName = setFlag fILE_NOTIFY_CHANGE_DIR_NAME
182+setRootMoved :: Toggle -> Config -> Config
183+setRootMoved = setFlag fILE_NOTIFY_CHANGE_DIR_NAME
184184
185185-- | Report when a file attribute is modified.
186186--
@@ -227,7 +227,7 @@ setModifiedSecurity = setFlag fILE_NOTIFY_CHANGE_SECURITY
227227setAllEvents :: Toggle -> Config -> Config
228228setAllEvents s =
229229 setModifiedFileName s
230− . setModifiedDirName s
230+ . setRootMoved s
231231 . setModifiedAttribute s
232232 . setModifiedSize s
233233 . setModifiedLastWrite s
@@ -399,32 +399,6 @@ utf8ToStringList = NonEmpty.map utf8ToString
399399closePathHandleStream :: SerialT IO (HANDLE, FilePath, Config) -> IO ()
400400closePathHandleStream = S.mapM_ (\(h, _, _) -> closeHandle h)
401401
402−-- | Start monitoring a list of file system paths for file system events with
403−-- the supplied configuration operation over the 'defaultConfig'. The
404−-- paths could be files or directories. When the path is a directory, only the
405−-- files and directories directly under the watched directory are monitored,
406−-- contents of subdirectories are not monitored. Monitoring starts from the
407−-- current time onwards.
408−--
409−-- /Pre-release/
410−--
411−watchPathsWith ::
412− (Config -> Config)
413− -> NonEmpty (Array Word8)
414− -> SerialT IO Event
415−watchPathsWith f = watchTreesWith (f . setRecursiveMode False)
416−
417−-- | Like 'watchPathsWith' but uses the 'defaultConfig' options.
418−--
419−-- @
420−-- watchPaths = watchPathsWith id
421−-- @
422−--
423−-- /Pre-release/
424−--
425−watchPaths :: NonEmpty (Array Word8) -> SerialT IO Event
426−watchPaths = watchPathsWith id
427−
428402-- XXX
429403-- Document the path treatment for Linux/Windows/macOS modules.
430404-- Remove the utf-8 encoding requirement of paths? It can be encoding agnostic
@@ -450,9 +424,7 @@ watchTreesWith f paths =
450424
451425 where
452426
453− before =
454− let cfg = f $ setRecursiveMode True defaultConfig
455− in return $ pathsToHandles (utf8ToStringList paths) cfg
427+ before = return $ pathsToHandles (utf8ToStringList paths) $ f defaultConfig
456428 after = liftIO . closePathHandleStream
457429
458430-- | Like 'watchTreesWith' but uses the 'defaultConfig' options.
@@ -464,18 +436,29 @@ watchTreesWith f paths =
464436watchTrees :: NonEmpty (Array Word8) -> SerialT IO Event
465437watchTrees = watchTreesWith id
466438
467−-- | Add a trailing "\" at the end of the path if there is none. Do not add a
468−-- "\" if the path is empty.
439+-- | Start monitoring a list of file system paths for file system events with
440+-- the supplied recursive mode and configuration. The paths could be files or
441+-- directories. When recursive mode is True and the path is a directory, the
442+-- whole directory tree under it is watched recursively.
443+-- When recursive mode is False and the path is a directory, only the
444+-- files and directories directly under the watched directory are monitored,
445+-- contents of subdirectories are not monitored. Monitoring starts from the
446+-- current time onwards. The paths are specified as UTF-8 encoded 'Array' of
447+-- 'Word8'.
448+--
449+-- @
450+-- watch True
451+-- ('setModifiedAttribute' On . 'setModifiedLastWrite' Off) defaultConfig
452+-- [Array.fromCString\# "dir"#]
453+-- @
454+--
455+-- /Pre-release/
469456--
470−ensureTrailingSlash :: String -> String
471−ensureTrailingSlash path =
472− if null path
473− then path
474− else
475− let x = last path
476− in if x /= '\\' && x /= '/'
477− then path ++ "\\"
478− else path
457+watch :: Bool -> Config -> NonEmpty (Array Word8) -> SerialT IO Event
458+watch rec cfg paths =
459+ case rec of
460+ True -> watchTreesWith (\_ -> cfg) paths
461+ False -> watchTreesWith (\_ -> setRecursiveMode False cfg) paths
479462
480463getFlag :: DWORD -> Event -> Bool
481464getFlag mask Event{..} = eventFlags == mask
@@ -487,8 +470,8 @@ getFlag mask Event{..} = eventFlags == mask
487470--
488471-- /Pre-release/
489472--
490−getRelPath :: Event -> String
491−getRelPath Event{..} = eventRelPath
473+getRelPath :: Event -> Array Word8
474+getRelPath Event{..} = (UTF8.toArray . UTF8.pack) eventRelPath
492475
493476-- XXX Change the type to Array Word8 to make it compatible with other APIs.
494477--
@@ -496,17 +479,10 @@ getRelPath Event{..} = eventRelPath
496479--
497480-- /Pre-release/
498481--
499−getRoot :: Event -> String
500−getRoot Event{..} = ensureTrailingSlash eventRootPath
482+getRoot :: Event -> Array Word8
483+getRoot Event{..} = (UTF8.toArray . UTF8.pack) eventRootPath
501484
502−-- XXX Change the type to Array Word8 to make it compatible with other APIs.
503−--
504−-- | Get the absolute file system object path for which the event is generated.
505−-- The path is a UTF-8 encoded array of bytes.
506−--
507−-- /Pre-release/
508−--
509−getAbsPath :: Event -> String
485+getAbsPath :: Event -> Array Word8
510486getAbsPath ev = getRoot ev <> getRelPath ev
511487
512488-- XXX need to document the exact semantics of these.
@@ -568,9 +544,8 @@ isOverflow Event{..} = totalBytes == 0
568544showEvent :: Event -> String
569545showEvent ev@Event{..} =
570546 "--------------------------"
571− ++ "\nRoot = " ++ show (getRoot ev)
572− ++ "\nRelative Path = " ++ show (getRelPath ev)
573− ++ "\nAbsolute Path = " ++ show (getAbsPath ev)
547+ ++ "\nRoot = " ++ utf8ToString (getRoot ev)
548+ ++ "\nPath = " ++ utf8ToString (getRelPath ev)
574549 ++ "\nFlags " ++ show eventFlags
575550 ++ showev isOverflow "Overflow"
576551 ++ showev isCreated "Created"
streamly.cabal+2
@@ -499,6 +499,7 @@ library
499499 , Streamly.Internal.FileSystem.File
500500 , Streamly.Internal.FileSystem.FDIO
501501 , Streamly.Internal.FileSystem.FD
502+ , Streamly.Internal.FileSystem.Event
502503
503504 -- streamly-console
504505 , Streamly.Internal.Console.Stdio
@@ -528,6 +529,7 @@ library
528529 , transformers >= 0.4 && < 0.6
529530
530531 , heaps >= 0.3 && < 0.5
532+ , filepath >= 1.2.0.0 && < 1.4.3.0
531533
532534 -- concurrency
533535 , atomic-primops >= 0.8 && < 0.9
test/Streamly/Test/FileSystem/Event.hs+13−15
@@ -13,6 +13,7 @@ import Control.Monad.IO.Class (MonadIO)
1313#if !defined(CABAL_OS_WINDOWS)
1414import Data.Char (ord)
1515#endif
16+import Data.Functor.Identity (runIdentity)
1617import Data.Maybe (fromJust)
1718import Data.Word (Word8)
1819import System.Directory
@@ -38,6 +39,7 @@ import qualified Streamly.Internal.Data.Array.Foreign as Array
3839import qualified Streamly.Internal.Data.Fold as FL
3940import qualified Streamly.Internal.Data.Parser as PR
4041import qualified Streamly.Internal.Data.Stream.IsStream as S
42+import qualified Streamly.Internal.Unicode.Stream as U
4143#if defined(CABAL_OS_DARWIN)
4244import qualified Streamly.Internal.FileSystem.Event.Darwin as Event
4345#elif defined(CABAL_OS_LINUX)
@@ -45,12 +47,7 @@ import qualified Streamly.Internal.FileSystem.Event.Linux as Event
4547#elif defined(CABAL_OS_WINDOWS)
4648import qualified Streamly.Internal.FileSystem.Event.Windows as Event
4749#else
48−#error "FS Events not supported on this platform
49−#endif
50−
51−#if !defined(CABAL_OS_WINDOWS)
52−import Data.Functor.Identity (runIdentity)
53−import qualified Streamly.Internal.Unicode.Stream as U
50+#error "FS Events not supported on this platform"
5451#endif
5552
5653import Test.Hspec
@@ -62,10 +59,8 @@ import Test.Hspec
6259toUtf8 :: MonadIO m => String -> m (Array Word8)
6360toUtf8 = Array.fromStream . Unicode.encodeUtf8' . S.fromList
6461
65−#if !defined(CABAL_OS_WINDOWS)
6662utf8ToString :: Array Word8 -> String
6763utf8ToString = runIdentity . S.toList . U.decodeUtf8' . Array.toStream
68−#endif
6964
7065timeout :: IO String
7166timeout = threadDelay 5000000 >> return "Timeout"
@@ -73,13 +68,16 @@ timeout = threadDelay 5000000 >> return "Timeout"
7368fseventDir :: String
7469fseventDir = "fsevent_dir"
7570
71+eoTask :: String
72+eoTask = "EOTask"
73+
7674-- XXX Make the getRelPath type same on windows and other platforms
7775eventPredicate :: Event.Event -> Bool
7876eventPredicate ev =
7977#if defined(CABAL_OS_WINDOWS)
80− if (Event.getRelPath ev) == "EOTask"
78+ if (utf8ToString $ Event.getRelPath ev) == eoTask
8179#else
82− if (utf8ToString $ Event.getRelPath ev) == "EOTask"
80+ if (utf8ToString $ Event.getRelPath ev) == eoTask
8381#endif
8482 then False
8583 else True
@@ -112,16 +110,16 @@ showEventShort :: Event.Event -> String
112110#if defined(CABAL_OS_WINDOWS)
113111-- | Convert an 'Event' record to a short representation for unit test.
114112showEventShort ev@Event.Event{..} =
115− Event.getRelPath ev ++ "_" ++ show eventFlags
113+ (utf8ToString $ Event.getRelPath ev) ++ "_" ++ show eventFlags
116114#elif defined(CABAL_OS_LINUX)
117115showEventShort ev@Event.Event{..} =
118− (utf8ToString $ removeTrailingSlash $ Event.getRelPath ev)
116+ utf8ToString (removeTrailingSlash $ Event.getRelPath ev)
119117 ++ "_" ++ show eventFlags
120118 ++ showev Event.isDir "Dir"
121119
122120 where showev f str = if f ev then "_" ++ str else ""
123121#else
124−#error "Unsupported OS
122+#error "Unsupported OS"
125123#endif
126124
127125-------------------------------------------------------------------------------
@@ -139,7 +137,7 @@ checkEvents rootPath m matchList = do
139137 let eventStr = map showEventShort events
140138 let baseSet = Set.fromList matchList
141139 resultSet = Set.fromList eventStr
142− if (baseSet `Set.isSubsetOf` resultSet)
140+ if baseSet `Set.isSubsetOf` resultSet
143141 then
144142 return "PASS"
145143 else do
@@ -399,4 +397,4 @@ moduleName = "FileSystem.Event"
399397main :: IO ()
400398main = do
401399 hSetBuffering stdout NoBuffering
402− hspec $ describe moduleName $ sequence_ $ map driver testDesc
400+ hspec $ describe moduleName $ mapM_ driver testDesc