@@ -0,0 +1,832 @@
2+-- Module : Streamly.Test.FileSystem.Event.Linux
3+-- Copyright : (c) 2020 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
9+module Streamly.Test.FileSystem.Event.Linux (testAllEvents, testCommonEvents) where
11+import Control.Concurrent (MVar, newEmptyMVar, putMVar, takeMVar, threadDelay)
12+import Control.Monad.IO.Class (MonadIO)
13+import Data.Char (ord)
14+import Data.Functor.Identity (runIdentity)
15+import Data.List.NonEmpty (NonEmpty)
16+import Data.Maybe (fromJust)
17+import Data.Word (Word8)
18+import System.Directory
19+ ( createDirectoryIfMissing
20+ , createDirectoryLink
27+import System.FilePath ((</>))
28+import System.IO.Temp (withSystemTempDirectory)
29+import System.IO.Unsafe (unsafePerformIO)
30+import Streamly.Internal.Data.Array.Foreign (Array)
32+import qualified Data.List.NonEmpty as NonEmpty
33+import qualified Data.Set as Set
34+import qualified Streamly.Unicode.Stream as Unicode
35+import qualified Streamly.Internal.Data.Array.Foreign as Array
36+import qualified Streamly.Internal.Data.Fold as FL
37+import qualified Streamly.Internal.Data.Parser as PR
38+import qualified Streamly.Internal.Data.Stream.IsStream as S
39+import qualified Streamly.Internal.Unicode.Stream as U
40+import qualified Streamly.Internal.FileSystem.Event.Linux as Event
44+-------------------------------------------------------------------------------
46+-------------------------------------------------------------------------------
48+toUtf8 :: MonadIO m => String -> m (Array Word8)
49+toUtf8 = Array.fromStream . Unicode.encodeUtf8' . S.fromList
51+utf8ToString :: Array Word8 -> String
52+utf8ToString = runIdentity . S.toList . U.decodeUtf8' . Array.toStream
55+timeout = threadDelay 5000000 >> return "Timeout"
58+fseventDir = "fsevent_dir"
63+-- XXX Make the getRelPath type same on windows and other platforms
64+eventPredicate :: Event.Event -> Bool
66+ if (utf8ToString $ Event.getRelPath ev) == eoTask
70+-------------------------------------------------------------------------------
71+-- Event matching utilities
72+-------------------------------------------------------------------------------
74+removeTrailingSlash :: Array Word8 -> Array Word8
75+removeTrailingSlash path =
76+ if Array.length path == 0
79+ let mx = Array.getIndex path (Array.length path - 1)
81+ Nothing -> error "removeTrailingSlash: Bug: Invalid index"
83+ if x == fromIntegral (ord '/')
84+ -- XXX need array slicing
86+ $ Array.fromStreamN (Array.length path - 1)
87+ $ Array.toStream path
91+-- XXX Return a tuple (path, flags) instead of appending flags to path. And
92+-- then check the flags using an event mask.
94+showEventShort :: Event.Event -> String
95+showEventShort ev@Event.Event{..} =
96+ utf8ToString (removeTrailingSlash $ Event.getRelPath ev)
97+ ++ "_" ++ show eventFlags
98+ -- ++ showev Event.isDir "DirX"
99+ ++ showev Event.isEventsLost "Overflow"
101+ ++ showev Event.isRootUnwatched "RootUnwatched"
102+ ++ showev Event.isRootDeleted "RootDeleted"
103+ ++ showev Event.isRootMoved "RootMoved"
104+ ++ showev Event.isRootUnmounted "RootUnmounted"
106+ ++ showev Event.isMetadataChanged "MetadataChanged"
108+ ++ showev Event.isAccessed "Accessed"
109+ ++ showev Event.isOpened "Opened"
110+ ++ showev Event.isWriteClosed "WriteClosed"
111+ ++ showev Event.isNonWriteClosed "NonWriteClosed"
113+ ++ showev Event.isCreated "Created"
114+ ++ showev Event.isDeleted "Deleted"
115+ ++ showev Event.isModified "Modified"
116+ ++ showev Event.isMovedFrom "MovedFrom"
117+ ++ showev Event.isMovedTo "MovedTo"
118+ ++ showev Event.isDir "Dir"
120+ where showev f str = if f ev then "_" ++ str else ""
122+type EventChecker = FilePath -> MVar () -> [String] -> IO String
123+type EventWatch = NonEmpty (Array Word8) -> S.SerialT IO Event.Event
124+type ToEventList = S.SerialT IO Event.Event -> IO [Event.Event]
129+-------------------------------------------------------------------------------
131+-------------------------------------------------------------------------------
133+eventListWithFixLen :: Int -> ToEventList
134+eventListWithFixLen count = S.toList . S.take count --51/20
136+eventListWithEOtask :: ToEventList
137+eventListWithEOtask = S.parse (PR.takeWhile eventPredicate FL.toList)
139+checkEvents :: ToEventList -> EventWatch -> EventChecker
140+checkEvents toEL ew rootPath m matchList = do
141+ let args = [rootPath]
142+ paths <- mapM toUtf8 args
143+ putStrLn ("Watch started !!!! on Path " ++ rootPath)
145+ $ S.before (putMVar m ())
146+ $ ew (NonEmpty.fromList paths)
147+ let eventStr = map showEventShort events
148+ let baseSet = Set.fromList matchList
149+ resultSet = Set.fromList eventStr
150+ if baseSet `Set.isSubsetOf` resultSet
154+ putStrLn $ "baseSet " ++ show matchList
155+ putStrLn $ "resultSet " ++ show eventStr
158+-------------------------------------------------------------------------------
159+-- FS Event Generators
160+-------------------------------------------------------------------------------
162+checker :: S.IsStream t =>
163+ EventChecker -> FilePath -> MVar () -> [String] -> t IO String
164+checker ec rootPath synch matchList =
165+ S.fromEffect (ec rootPath synch matchList)
167+ S.fromEffect timeout
169+-------------------------------------------------------------------------------
171+-------------------------------------------------------------------------------
182+driver ec (desc, pre, ops, events, sym) = it desc $ runTest `shouldReturn` "PASS"
187+ sync <- newEmptyMVar
188+ withSystemTempDirectory fseventDir $ \fp ->
191+ createDirectoryLink fp (fp ++ "SymLink")
192+ runTest0 (fp ++ "SymLink") sync
193+ else runTest0 fp sync
195+ runTest0 fp sync = do
197+ let eventStream = checker ec fp sync events
198+ fsOps = S.fromEffect $ runFSOps fp sync
199+ fmap fromJust $ S.head $ eventStream `S.parallelFst` fsOps
201+ runFSOps fp sync = do
205+ -- 'EOTask Created dir' event gets out of order so need to wait here
206+ threadDelay 200000 -- Why this delay?
207+ createDirectoryIfMissing True (fp </> eoTask)
208+ threadDelay 10000000
209+ error "fs ops timed out"
211+-------------------------------------------------------------------------------
213+-------------------------------------------------------------------------------
216+ testDescRemoveRootDir,
217+ testDescRemoveRootDirSymLink,
218+ testDescMoveRootDir,
220+ testDescMoveRootDirCommon,
221+ testDescRemoveRootDirCommon,
222+ testDescRemoveRootDirSymLinkCommon ::
223+ [ ( String -- test description
224+ , FilePath -> IO () -- pre test operation
225+ , FilePath -> IO () -- file system actions
226+ , [String] -- expected events
231+ ( "Create a single directory"
233+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
234+ , [ "_1073741856_Opened_Dir"
235+ , "_1073741825_Accessed_Dir"
236+ , "_1073741840_NonWriteClosed_Dir"
237+ , "dir1Single_1073742080_Created_Dir"
238+ , "dir1Single_1073741856_Opened_Dir"
239+ , "dir1Single_1073741825_Accessed_Dir"
240+ , "dir1Single_1073741840_NonWriteClosed_Dir"
244+ , ( "Remove a single directory"
245+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
246+ , \fp -> removeDirectory (fp </> "dir1Single")
247+ , [ "_1073741856_Opened_Dir"
248+ , "_1073741825_Accessed_Dir"
249+ , "_1073741840_NonWriteClosed_Dir"
250+ , "dir1Single_1073741856_Opened_Dir"
251+ , "dir1Single_1073741825_Accessed_Dir"
252+ , "dir1Single_1073741840_NonWriteClosed_Dir"
253+ , "dir1Single_1024_RootDeleted"
254+ , "dir1Single_32768_RootUnwatched"
255+ , "dir1Single_1073742336_Deleted_Dir"
259+ , ( "Rename a single directory"
260+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
262+ let spath = fp </> "dir1Single"
263+ tpath = fp </> "dir1SingleRenamed"
264+ in renameDirectory spath tpath
265+ , [ "_1073741856_Opened_Dir"
266+ , "_1073741825_Accessed_Dir"
267+ , "_1073741840_NonWriteClosed_Dir"
268+ , "dir1Single_1073741856_Opened_Dir"
269+ , "dir1Single_1073741825_Accessed_Dir"
270+ , "dir1Single_1073741840_NonWriteClosed_Dir"
271+ , "dir1Single_1073741888_MovedFrom_Dir"
272+ , "dir1SingleRenamed_1073741952_MovedTo_Dir"
273+ , "dir1Single_2048_RootMoved"
277+ , ( "Create a nested directory"
280+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
281+ , [ "_1073741856_Opened_Dir"
282+ , "_1073741825_Accessed_Dir"
283+ , "_1073741840_NonWriteClosed_Dir"
284+ , "dir1_1073742080_Created_Dir"
285+ , "dir1_1073741856_Opened_Dir"
286+ , "dir1_1073741825_Accessed_Dir"
287+ , "dir1_1073741840_NonWriteClosed_Dir"
288+ , "dir1/dir2_1073741856_Opened_Dir"
289+ , "dir1/dir2_1073741825_Accessed_Dir"
290+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
291+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
292+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
293+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
297+ , ( "Rename a nested directory"
298+ , \fp -> createDirectoryIfMissing True
299+ (fp </> "dir1" </> "dir2" </> "dir3")
301+ let spath = fp </> "dir1" </> "dir2" </> "dir3"
302+ tpath = fp </> "dir1" </> "dir2" </> "dir3Renamed"
303+ in renameDirectory spath tpath
304+ , [ "_1073741856_Opened_Dir"
305+ , "_1073741825_Accessed_Dir"
306+ , "_1073741840_NonWriteClosed_Dir"
307+ , "dir1_1073741856_Opened_Dir"
308+ , "dir1_1073741825_Accessed_Dir"
309+ , "dir1_1073741840_NonWriteClosed_Dir"
310+ , "dir1/dir2_1073741856_Opened_Dir"
311+ , "dir1/dir2_1073741825_Accessed_Dir"
312+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
313+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
314+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
315+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
316+ , "dir1/dir2/dir3_1073741888_MovedFrom_Dir"
317+ , "dir1/dir2/dir3Renamed_1073741952_MovedTo_Dir"
318+ , "dir1/dir2/dir3_2048_RootMoved"
322+ , ( "Create a file in root Dir"
324+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
325+ , [ "_1073741856_Opened_Dir"
326+ , "_1073741825_Accessed_Dir"
327+ , "_1073741840_NonWriteClosed_Dir"
328+ , "FileCreated.txt_256_Created"
329+ , "FileCreated.txt_32_Opened"
330+ , "FileCreated.txt_2_Modified"
331+ , "FileCreated.txt_8_WriteClosed"
335+ , ( "Remove a file in root Dir"
336+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
337+ , \fp -> removeFile (fp </> "FileCreated.txt")
338+ , [ "_1073741856_Opened_Dir"
339+ , "_1073741825_Accessed_Dir"
340+ , "_1073741840_NonWriteClosed_Dir"
341+ , "FileCreated.txt_512_Deleted"
345+ , ( "Rename a file in root Dir"
346+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
348+ let spath = (fp </> "FileCreated.txt")
349+ tpath = (fp </> "FileRenamed.txt")
350+ in renamePath spath tpath
351+ , [ "_1073741856_Opened_Dir"
352+ , "_1073741825_Accessed_Dir"
353+ , "_1073741840_NonWriteClosed_Dir"
354+ , "FileCreated.txt_64_MovedFrom"
355+ , "FileRenamed.txt_128_MovedTo"
359+ , ( "Create a file in a nested Dir"
361+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
363+ let p = fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt"
364+ in writeFile p "Test Data"
365+ , [ "_1073741856_Opened_Dir"
366+ , "_1073741825_Accessed_Dir"
367+ , "_1073741840_NonWriteClosed_Dir"
368+ , "dir1_1073741856_Opened_Dir"
369+ , "dir1_1073741825_Accessed_Dir"
370+ , "dir1_1073741840_NonWriteClosed_Dir"
371+ , "dir1/dir2_1073741856_Opened_Dir"
372+ , "dir1/dir2_1073741825_Accessed_Dir"
373+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
374+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
375+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
376+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
377+ , "dir1/dir2/dir3/FileCreated.txt_256_Created"
378+ , "dir1/dir2/dir3/FileCreated.txt_32_Opened"
379+ , "dir1/dir2/dir3/FileCreated.txt_2_Modified"
380+ , "dir1/dir2/dir3/FileCreated.txt_8_WriteClosed"
384+ , ( "Remove a file in a nested Dir"
386+ let nestedDir = fp </> "dir1" </> "dir2" </> "dir3"
387+ fpath = nestedDir </> "FileCreated.txt"
389+ createDirectoryIfMissing True nestedDir
390+ writeFile fpath "Test Data"
392+ let p = fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt"
394+ , [ "_1073741856_Opened_Dir"
395+ , "_1073741825_Accessed_Dir"
396+ , "_1073741840_NonWriteClosed_Dir"
397+ , "dir1_1073741856_Opened_Dir"
398+ , "dir1_1073741825_Accessed_Dir"
399+ , "dir1_1073741840_NonWriteClosed_Dir"
400+ , "dir1/dir2_1073741856_Opened_Dir"
401+ , "dir1/dir2_1073741825_Accessed_Dir"
402+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
403+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
404+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
405+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
406+ , "dir1/dir2/dir3/FileCreated.txt_512_Deleted"
410+ , ( "Rename a file in a nested Dir"
412+ let nestedDir = fp </> "dir1" </> "dir2" </> "dir3"
413+ fpath = nestedDir </> "FileCreated.txt"
415+ createDirectoryIfMissing True nestedDir
416+ writeFile fpath "Test Data"
418+ let s = (fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt")
419+ t = (fp </> "dir1" </> "dir2" </> "dir3" </> "FileRenamed.txt")
421+ , [ "_1073741856_Opened_Dir"
422+ , "_1073741825_Accessed_Dir"
423+ , "_1073741840_NonWriteClosed_Dir"
424+ , "dir1_1073741856_Opened_Dir"
425+ , "dir1_1073741825_Accessed_Dir"
426+ , "dir1_1073741840_NonWriteClosed_Dir"
427+ , "dir1/dir2_1073741856_Opened_Dir"
428+ , "dir1/dir2_1073741825_Accessed_Dir"
429+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
430+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
431+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
432+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
433+ , "dir1/dir2/dir3/FileCreated.txt_64_MovedFrom"
434+ , "dir1/dir2/dir3/FileRenamed.txt_128_MovedTo"
438+ , ( "Remove the nested directory"
440+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
441+ , \fp -> removePathForcibly (fp </> "dir1")
443+ , [ "_1073741856_Opened_Dir"
444+ , "_1073741825_Accessed_Dir"
445+ , "_1073741840_NonWriteClosed_Dir"
446+ , "dir1_1073741856_Opened_Dir"
447+ , "dir1_1073741825_Accessed_Dir"
448+ , "dir1_1073741840_NonWriteClosed_Dir"
449+ , "dir1/dir2_1073741856_Opened_Dir"
450+ , "dir1/dir2_1073741825_Accessed_Dir"
451+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
452+ , "dir1/dir2/dir3_1073741856_Opened_Dir"
453+ , "dir1/dir2/dir3_1073741825_Accessed_Dir"
454+ , "dir1/dir2/dir3_1073741840_NonWriteClosed_Dir"
455+ , "dir1_1073741828_MetadataChanged_Dir"
456+ , "dir1/dir2_1073741828_MetadataChanged_Dir"
457+ , "dir1/dir2/dir3_1073741828_MetadataChanged_Dir"
458+ , "dir1/dir2/dir3_1024_RootDeleted"
459+ , "dir1/dir2/dir3_32768_RootUnwatched"
460+ , "dir1/dir2/dir3_1073742336_Deleted_Dir"
461+ , "dir1/dir2_1024_RootDeleted"
462+ , "dir1/dir2_32768_RootUnwatched"
463+ , "dir1/dir2_1073742336_Deleted_Dir"
464+ , "dir1_1024_RootDeleted"
465+ , "dir1_32768_RootUnwatched"
466+ , "dir1_1073742336_Deleted_Dir"
472+testDescRemoveRootDir =
473+ [ ( "Remove the root directory"
475+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
476+ , \fp -> removePathForcibly fp
477+ , [ "_1073741856_Opened_Dir"
478+ , "_1073741825_Accessed_Dir"
479+ , "_1073741840_NonWriteClosed_Dir"
480+ , "dir1_1073741856_Opened_Dir"
481+ , "dir1_1073741825_Accessed_Dir"
482+ , "dir1_1073741840_NonWriteClosed_Dir"
483+ , "dir1/dir2_1073741856_Opened_Dir"
484+ , "dir1/dir2_1073741825_Accessed_Dir"
485+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
486+ , "_1073741828_MetadataChanged_Dir"
487+ , "dir1_1073741828_MetadataChanged_Dir"
488+ , "dir1/dir2_1073741828_MetadataChanged_Dir"
489+ , "dir1/dir2_1024_RootDeleted"
490+ , "dir1/dir2_32768_RootUnwatched"
491+ , "dir1/dir2_1073742336_Deleted_Dir"
492+ , "dir1_1024_RootDeleted"
493+ , "dir1_32768_RootUnwatched"
494+ , "dir1_1073742336_Deleted_Dir"
495+ , "_1024_RootDeleted"
501+testDescMoveRootDir =
503+ ( "Moved the root directory"
505+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
506+ , \fp -> renameDirectory (fp </> "dir1" </> "dir2") (fp </> "dir1" </> "dir2_Moved")
507+ , [ "_1073741856_Opened_Dir"
508+ ,"_1073741825_Accessed_Dir"
509+ ,"_1073741840_NonWriteClosed_Dir"
510+ ,"dir1_1073741856_Opened_Dir"
511+ ,"dir1_1073741825_Accessed_Dir"
512+ ,"dir1_1073741840_NonWriteClosed_Dir"
513+ ,"dir1/dir2_1073741856_Opened_Dir"
514+ ,"dir1/dir2_1073741825_Accessed_Dir"
515+ ,"dir1/dir2_1073741840_NonWriteClosed_Dir"
516+ ,"dir1/dir2_1073741888_MovedFrom_Dir"
517+ ,"dir1/dir2_Moved_1073741952_MovedTo_Dir"
518+ ,"dir1/dir2_2048_RootMoved"
524+testDescRemoveRootDirSymLink =
525+ [ ( "Remove the root directory as SymLink"
527+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
528+ , \fp -> removePathForcibly fp
529+ , [ "_1073741856_Opened_Dir"
530+ , "_1073741825_Accessed_Dir"
531+ , "_1073741840_NonWriteClosed_Dir"
532+ , "dir1_1073741856_Opened_Dir"
533+ , "dir1_1073741825_Accessed_Dir"
534+ , "dir1_1073741840_NonWriteClosed_Dir"
535+ , "dir1/dir2_1073741856_Opened_Dir"
536+ , "dir1/dir2_1073741825_Accessed_Dir"
537+ , "dir1/dir2_1073741840_NonWriteClosed_Dir"
538+ , "_1073741828_MetadataChanged_Dir"
544+-------------------------Test Descriptor for common Event Types ---------------
548+ ( "Create a single directory"
550+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
551+ , ["dir1Single_1073742080_Created_Dir"]
554+ , ( "Remove a single directory"
555+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
556+ , \fp -> removeDirectory (fp </> "dir1Single")
557+ , [ "dir1Single_1024_RootDeleted"
558+ , "dir1Single_32768_RootUnwatched"
559+ , "dir1Single_1073742336_Deleted_Dir"
563+ , ( "Rename a single directory"
564+ , \fp -> createDirectoryIfMissing True (fp </> "dir1Single")
566+ let spath = fp </> "dir1Single"
567+ tpath = fp </> "dir1SingleRenamed"
568+ in renameDirectory spath tpath
569+ , [ "dir1Single_1073741888_MovedFrom_Dir"
570+ , "dir1SingleRenamed_1073741952_MovedTo_Dir"
571+ , "dir1Single_2048_RootMoved"
575+ , ( "Create a nested directory"
578+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
579+ , ["dir1_1073742080_Created_Dir"]
582+ , ( "Rename a nested directory"
583+ , \fp -> createDirectoryIfMissing True
584+ (fp </> "dir1" </> "dir2" </> "dir3")
586+ let spath = fp </> "dir1" </> "dir2" </> "dir3"
587+ tpath = fp </> "dir1" </> "dir2" </> "dir3Renamed"
588+ in renameDirectory spath tpath
589+ , [ "dir1/dir2/dir3_1073741888_MovedFrom_Dir"
590+ , "dir1/dir2/dir3Renamed_1073741952_MovedTo_Dir"
591+ , "dir1/dir2/dir3_2048_RootMoved"
595+ , ( "Create a file in root Dir"
597+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
598+ , [ "FileCreated.txt_256_Created"
599+ , "FileCreated.txt_2_Modified"
603+ , ( "Remove a file in root Dir"
604+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
605+ , \fp -> removeFile (fp </> "FileCreated.txt")
606+ , ["FileCreated.txt_512_Deleted"]
609+ , ( "Rename a file in root Dir"
610+ , \fp -> writeFile (fp </> "FileCreated.txt") "Test Data"
612+ let spath = (fp </> "FileCreated.txt")
613+ tpath = (fp </> "FileRenamed.txt")
614+ in renamePath spath tpath
615+ , [ "FileCreated.txt_64_MovedFrom"
616+ , "FileRenamed.txt_128_MovedTo"
620+ , ( "Create a file in a nested Dir"
622+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
624+ let p = fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt"
625+ in writeFile p "Test Data"
626+ , [ "dir1/dir2/dir3/FileCreated.txt_256_Created"
627+ , "dir1/dir2/dir3/FileCreated.txt_2_Modified"
631+ , ( "Remove a file in a nested Dir"
633+ let nestedDir = fp </> "dir1" </> "dir2" </> "dir3"
634+ fpath = nestedDir </> "FileCreated.txt"
636+ createDirectoryIfMissing True nestedDir
637+ writeFile fpath "Test Data"
639+ let p = fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt"
641+ , ["dir1/dir2/dir3/FileCreated.txt_512_Deleted"]
644+ , ( "Rename a file in a nested Dir"
646+ let nestedDir = fp </> "dir1" </> "dir2" </> "dir3"
647+ fpath = nestedDir </> "FileCreated.txt"
649+ createDirectoryIfMissing True nestedDir
650+ writeFile fpath "Test Data"
652+ let s = (fp </> "dir1" </> "dir2" </> "dir3" </> "FileCreated.txt")
653+ t = (fp </> "dir1" </> "dir2" </> "dir3" </> "FileRenamed.txt")
655+ , [ "dir1/dir2/dir3/FileCreated.txt_64_MovedFrom"
656+ , "dir1/dir2/dir3/FileRenamed.txt_128_MovedTo"
660+ , ( "Remove the nested directory"
662+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2" </> "dir3")
663+ , \fp -> removePathForcibly (fp </> "dir1")
665+ , [ "dir1_1073741828_MetadataChanged_Dir"
666+ , "dir1/dir2_1073741828_MetadataChanged_Dir"
667+ , "dir1/dir2/dir3_1073741828_MetadataChanged_Dir"
668+ , "dir1/dir2/dir3_1024_RootDeleted"
669+ , "dir1/dir2/dir3_32768_RootUnwatched"
670+ , "dir1/dir2/dir3_1073742336_Deleted_Dir"
671+ , "dir1/dir2_1024_RootDeleted"
672+ , "dir1/dir2_32768_RootUnwatched"
673+ , "dir1/dir2_1073742336_Deleted_Dir"
674+ , "dir1_1024_RootDeleted"
675+ , "dir1_32768_RootUnwatched"
676+ , "dir1_1073742336_Deleted_Dir"
682+testDescRemoveRootDirCommon =
683+ [ ( "Remove the root directory"
685+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
686+ , \fp -> removePathForcibly fp
687+ , [ "_1073741828_MetadataChanged_Dir"
688+ , "dir1_1073741828_MetadataChanged_Dir"
689+ , "dir1/dir2_1073741828_MetadataChanged_Dir"
690+ , "dir1/dir2_1024_RootDeleted"
691+ , "dir1/dir2_32768_RootUnwatched"
692+ , "dir1/dir2_1073742336_Deleted_Dir"
693+ , "dir1_1024_RootDeleted"
694+ , "dir1_32768_RootUnwatched"
695+ , "dir1_1073742336_Deleted_Dir"
696+ , "_1024_RootDeleted"
702+testDescMoveRootDirCommon =
704+ ( "Moved the root directory"
706+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
709+ (fp </> "dir1" </> "dir2")
710+ (fp </> "dir1" </> "dir2_Moved")
711+ , [ "dir1/dir2_1073741888_MovedFrom_Dir"
712+ , "dir1/dir2_Moved_1073741952_MovedTo_Dir"
713+ , "dir1/dir2_2048_RootMoved"
719+testDescRemoveRootDirSymLinkCommon =
720+ [ ( "Remove the root directory as SymLink"
722+ createDirectoryIfMissing True (fp </> "dir1" </> "dir2")
723+ , \fp -> removePathForcibly fp
724+ , ["_1073741828_MetadataChanged_Dir"]
728+-------------------------------------------------------------------------------
730+moduleName = "FileSystem.Event.Linux"
732+setAllEvents :: Event.Config -> Event.Config
733+setAllEvents cfg = ( Event.setAccessed Event.On
734+ . Event.setOpened Event.On
735+ . Event.setWriteClosed Event.On
736+ . Event.setNonWriteClosed Event.On
739+allEvents :: NonEmpty (Array Word8) -> S.SerialT IO Event.Event
740+allEvents = Event.watchWith setAllEvents
742+-- Test cases for Linux platform
744+testAllEvents :: IO ()
746+ -- Test cases for regular path
748+ $ describe moduleName
750+ (driver $ checkEvents eventListWithEOtask allEvents)
753+ -- Test cases for SymLink path
755+ $ describe moduleName
757+ (driver $ checkEvents eventListWithEOtask allEvents)
758+ (map (\(a, b, c, d, _) -> (a, b, c, d, True)) testDesc)
760+ -- Test cases for moving root path
762+ $ describe moduleName
764+ (driver $ checkEvents eventListWithEOtask allEvents)
767+ -- Test cases for moving root path as SymLink
769+ $ describe moduleName
771+ (driver $ checkEvents eventListWithEOtask allEvents)
772+ (map (\(a, b, c, d, _) -> (a, b, c, d, True)) testDescMoveRootDir)
774+ -- Test cases for removing root path
776+ $ describe moduleName
778+ (driver $ checkEvents (eventListWithFixLen 51) allEvents)
779+ testDescRemoveRootDir
781+ -- Test cases for removing root path as Symlink
783+ $ describe moduleName
785+ (driver $ checkEvents (eventListWithFixLen 20) allEvents)
786+ testDescRemoveRootDirSymLink
788+-- Test cases for Events which are common across the platforms
790+testCommonEvents :: IO ()
791+testCommonEvents = do
792+ -- Test cases for regular path
794+ $ describe moduleName
796+ (driver $ checkEvents eventListWithEOtask $ Event.watchRecursive)
799+ -- Test cases for SymLink path
801+ $ describe moduleName
803+ (driver $ checkEvents eventListWithEOtask Event.watchRecursive)
804+ (map (\(a, b, c, d, _) -> (a, b, c, d, True)) testDescCommon)
806+ -- Test cases for moving root path
808+ $ describe moduleName
810+ (driver $ checkEvents eventListWithEOtask Event.watchRecursive)
811+ testDescMoveRootDirCommon
813+ -- Test cases for moving root path as SymLink
815+ $ describe moduleName
817+ (driver $ checkEvents eventListWithEOtask Event.watchRecursive)
818+ (map (\(a, b, c, d, _) -> (a, b, c, d, True)) testDescMoveRootDirCommon)
820+ -- Test cases for removing root path
822+ $ describe moduleName
824+ (driver $ checkEvents (eventListWithFixLen 12) Event.watchRecursive)
825+ testDescRemoveRootDirCommon
827+ -- Test cases for removing root path as Symlink
829+ $ describe moduleName
831+ (driver $ checkEvents (eventListWithFixLen 1) Event.watchRecursive)
832+ testDescRemoveRootDirSymLinkCommon