FileSystem (Polyglot)¶

In [ ]:
#r @"../../../../../../../.nuget/packages/fsharp.control.asyncseq/3.2.1/lib/netstandard2.1/FSharp.Control.AsyncSeq.dll"
#r @"../../../../../../../.nuget/packages/system.reactive/6.0.1-preview.1/lib/net6.0/System.Reactive.dll"
#r @"../../../../../../../.nuget/packages/system.reactive.linq/6.0.1-preview.1/lib/netstandard2.0/System.Reactive.Linq.dll"
#r @"../../../../../../../.nuget/packages/argu/6.2.4/lib/netstandard2.0/Argu.dll"
In [ ]:
#!import ../../lib/fsharp/Notebooks.dib
#!import ../../lib/fsharp/Testing.dib
In [ ]:
#!import ../../lib/fsharp/Common.fs
#!import ../../lib/fsharp/CommonFSharp.fs
#!import ../../lib/fsharp/Async.fs
#!import ../../lib/fsharp/AsyncSeq.fs
#!import ../../lib/fsharp/Runtime.fs
In [ ]:
#if !INTERACTIVE
open Lib
#endif
In [ ]:
open Common
open SpiralFileSystem.Operators

watchDirectory¶

In [ ]:
[<RequireQualifiedAccess>]
type FileSystemChangeType =
    | Failure
    | Changed
    | Created
    | Deleted
    | Renamed

[<RequireQualifiedAccess>]
type FileSystemChange =
    | Failure of exn: exn
    | Changed of path: string * content: string option
    | Created of path: string * content: string option
    | Deleted of path: string
    | Renamed of oldPath: string * (string * string option)


let inline watchDirectoryWithFilter filter shouldReadContent path =
    let fullPath = path |> System.IO.Path.GetFullPath
    let _locals () = $"filter: {filter} / {_locals ()}"

    let watcher =
        new System.IO.FileSystemWatcher (
            Path = fullPath,
            NotifyFilter = filter,
            EnableRaisingEvents = true,
            IncludeSubdirectories = true
        )

    let inline getEventPath (path : string) =
        path |> SpiralSm.trim |> SpiralSm.replace fullPath "" |> SpiralSm.trim_start [| '/'; '\\' |]

    let inline ticks () =
        System.DateTime.UtcNow.Ticks

    let changedStream =
        AsyncSeq.subscribeEvent
            watcher.Changed
            (fun event ->
                ticks (),
                [ FileSystemChange.Changed (getEventPath event.FullPath, None) ]
            )

    let deletedStream =
        AsyncSeq.subscribeEvent
            watcher.Deleted
            (fun event ->
                ticks (),
                [ FileSystemChange.Deleted (getEventPath event.FullPath) ]
            )

    let createdStream =
        AsyncSeq.subscribeEvent
            watcher.Created
            (fun event ->
                let path = getEventPath event.FullPath
                ticks (), [
                    FileSystemChange.Created (path, None)
                    if SpiralPlatform.is_windows () then
                        FileSystemChange.Changed (path, None)
                ])

    let renamedStream =
        AsyncSeq.subscribeEvent
            watcher.Renamed
            (fun event ->
                ticks (), [
                    FileSystemChange.Renamed (
                        getEventPath event.OldFullPath,
                        (getEventPath event.FullPath, None)
                    )
                ]
            )

    let failureStream =
        AsyncSeq.subscribeEvent
            watcher.Error
            (fun event -> ticks (), [ FileSystemChange.Failure (event.GetException ()) ])

    let stream =
        [
            changedStream
            deletedStream
            createdStream
            renamedStream
            failureStream
        ]
        |> FSharp.Control.AsyncSeq.mergeAll
        |> FSharp.Control.AsyncSeq.map (fun (t, events) ->
            events
            |> List.fold
                (fun (i, events) event ->
                    i + 1L,
                    (t + i, event) :: events)
                (0L, [])
            |> snd
            |> List.rev
        )
        |> FSharp.Control.AsyncSeq.concatSeq
        |> FSharp.Control.AsyncSeq.mapAsyncParallel (fun (t, event) -> async {
            match shouldReadContent event, event with
            | true, FileSystemChange.Changed (path, _) ->
                do! Async.Sleep 5
                let! content = fullPath </> path |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Changed (path, content)
            | true, FileSystemChange.Created (path, _) ->
                do! Async.Sleep 5
                let! content = fullPath </> path |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Created (path, content)
            | true, FileSystemChange.Renamed (oldPath, (newPath, _)) ->
                let! content = fullPath </> newPath |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Renamed (oldPath, (newPath, content))
            | _ -> return t, event
        })

    let disposable =
        new_disposable (fun () ->
            trace Debug (fun () -> "FileSystem.watchWithFilter / Disposing watch stream") _locals
            watcher.EnableRaisingEvents <- false
            watcher.Dispose ()
        )

    stream, disposable

let inline watchDirectory path =
    watchDirectoryWithFilter
        (System.IO.NotifyFilters.FileName
        // ||| System.IO.NotifyFilters.DirectoryName
        // ||| System.IO.NotifyFilters.Attributes
        //// ||| System.IO.NotifyFilters.Size
        ||| System.IO.NotifyFilters.LastWrite
        //// ||| System.IO.NotifyFilters.LastAccess
        // ||| System.IO.NotifyFilters.CreationTime
        // ||| System.IO.NotifyFilters.Security
        )
        path

testEventsRaw (test)¶

In [ ]:
//// test

let inline testEventsRaw
    (watchFn : (_ -> bool) -> string -> FSharp.Control.AsyncSeq<int64 * FileSystemChange> * IDisposable)
    write
    =
    let struct (tempDir, tempDisposable) =
        "FileSystem.testEventsRaw"
        |> SpiralCrypto.hash_text
        |> SpiralFileSystem.create_temp_dir'
    let stream, disposable = watchFn (fun _ -> true) tempDir

    let events = System.Collections.Concurrent.ConcurrentBag ()

    let inline iter () =
        stream
        |> FSharp.Control.AsyncSeq.iterAsyncParallel (fun event -> async { events.Add event })

    let run = async {
        let! _ = iter () |> Async.StartChild
        do! Async.Sleep 250
        return! write tempDir
    }

    try
        run
        |> Async.runWithTimeout 60000
        |> _assertEqual (Some ())
    finally
        disposable.Dispose ()
        tempDisposable.Dispose ()

    let eventsLog =
        events
        |> Seq.toList
        |> List.sortBy fst
        |> List.fold
            (fun (prev, acc) (ticks, event) ->
                ticks, (ticks, (if prev = 0L then 0L else ticks - prev), event) :: acc
            )
            (0L, [])
        |> snd
        |> List.rev
        |> List.map (fun (diff, n, event) -> $"{n} / {diff} / {event}" |> SpiralSm.ellipsis_end 100L)
        |> SpiralSm.concat "\n"
    let _locals () = $"eventsLog: \n{eventsLog} / {_locals ()}"
    trace Debug (fun () -> "FileSystem.testEventsRaw") _locals

    events
    |> Seq.toList
    |> List.sortBy fst
    |> List.map snd
    |> List.fold
        (fun acc event ->
            match acc, event with
            | FileSystemChange.Changed (lastPath, Some lastContent) as lastEvent :: acc,
                FileSystemChange.Changed (path, Some content)
                when lastPath = path && content |> SpiralSm.starts_with lastContent
                ->
                event :: acc
            | _ -> event :: acc
        )
        []
    |> List.rev

fast (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"a{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! $"b{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 250

    for i = 1 to n do
        do! $"c{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 250
}

let inline run () =
    let events = testEventsRaw watchDirectory write

    events
    |> _sequenceEqual [
        FileSystemChange.Created ("file1.txt", Some "a1")
        FileSystemChange.Changed ("file1.txt", Some "a1")
        FileSystemChange.Created ("file2.txt", Some "a2")
        FileSystemChange.Changed ("file2.txt", Some "a2")

        FileSystemChange.Changed ("file1.txt", Some "b1")
        FileSystemChange.Changed ("file2.txt", Some "b2")

        FileSystemChange.Renamed ("file1.txt", ("file_1.txt", Some "b1"))
        FileSystemChange.Renamed ("file2.txt", ("file_2.txt", Some "b2"))

        FileSystemChange.Changed ("file_1.txt", Some "c1")
        FileSystemChange.Changed ("file_2.txt", Some "c2")

        FileSystemChange.Deleted "file_1.txt"
        FileSystemChange.Deleted "file_2.txt"
    ]

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:03 d #1 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
00:00:03 d #2 FileSystem.testEventsRaw / eventsLog: 
0 / 638859607878752173 / Created ("file1.txt", Some "a1")
12090 / 638859607878764263 / Changed ("file1.txt", Some "a1")
1550 / 638859607878765813 / Created ("file2.txt", Some "a2")
45 / 638859607878765858 / Changed ("file2.txt", Some "a2")
2475825 / 638859607881241683 / Changed ("file1.txt", Some "b1")
547 / 638859607881242230 / Changed ("file1.txt", Some "b1")
4958 / 638859607881247188 / Changed ("file2.txt", Some "b2")
238 / 638859607881247426 / Changed ("file2.txt", Some "b2")
2556224 / 638859607883803650 / Renamed ("file1.txt", ("file_1.txt", Some "b1"))
8930 / 638859607883812580 / Renamed ("file2.txt", ("file_2.txt", Some "b2"))
2518549 / 638859607886331129 / Changed ("file_1.txt", Some "c1")
1591 / 638859607886332720 / Changed ("file_1.txt", Some "c1")
4917 / 638859607886337637 / Changed ("file_2.txt", Some "c2")
599 / 638859607886338236 / Changed ("file_2.txt", Some "c2")
2528208 / 638859607888866444 / Deleted "file_1.txt"
1091 / 638859607888867535 / Deleted "file_2.txt"
[Created ("file1.txt", Some "a1"); Changed ("file1.txt", Some "a1"); Created ("file2.txt", Some "a2");
 Changed ("file2.txt", Some "a2"); Changed ("file1.txt", Some "b1"); Changed ("file2.txt", Some "b2");
 Renamed ("file1.txt", ("file_1.txt", Some "b1")); Renamed ("file2.txt", ("file_2.txt", Some "b2"));
 Changed ("file_1.txt", Some "c1"); Changed ("file_2.txt", Some "c2"); Deleted "file_1.txt"; Deleted "file_2.txt"]

Some ()

slow (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    let contents =
        [ 1 .. n ]
        |> List.map (string >> String.replicate 1_000_000)

    for i = 1 to n do
        do! $"{contents.[i - 1]}a" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! $"{contents.[i - 1]}b" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 1500

    for i = 1 to n do
        do! $"{contents.[i - 1]}c" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 1500
}

let inline run () =
    let events =
        testEventsRaw watchDirectory write
        |> List.map (function
            | FileSystemChange.Changed (path, Some content) ->
                FileSystemChange.Changed (path, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
            | FileSystemChange.Created (path, Some content) ->
                FileSystemChange.Created (path, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
            | FileSystemChange.Renamed (oldPath, (newPath, Some content)) ->
                FileSystemChange.Renamed (
                    oldPath,
                    (newPath, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
                )
            | event -> event
        )

    events
    |> _sequenceEqual [
        FileSystemChange.Created ("file1.txt", Some "1a")
        FileSystemChange.Changed ("file1.txt", Some "1a")
        FileSystemChange.Created ("file2.txt", Some "2a")
        FileSystemChange.Changed ("file2.txt", Some "2a")

        FileSystemChange.Changed ("file1.txt", Some "1b")
        FileSystemChange.Changed ("file2.txt", Some "2b")

        FileSystemChange.Renamed ("file1.txt", ("file_1.txt", Some "1b"))
        FileSystemChange.Renamed ("file2.txt", ("file_2.txt", Some "2b"))

        FileSystemChange.Changed ("file_1.txt", Some "1c")
        FileSystemChange.Changed ("file_2.txt", Some "2c")

        FileSystemChange.Deleted "file_1.txt"
        FileSystemChange.Deleted "file_2.txt"
    ]

run
|> retry_fn 5
|> _assertEqual (Some ())
Some ()

00:00:12 d #3 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
00:00:13 d #4 FileSystem.testEventsRaw / eventsLog: 
0 / 638859607902957734 / Created
  ("file1.txt",
 ...11111111111111111111111111111111111111111111111a")
3176 / 638859607902960910 / Changed
  ("file1.txt"...11111111111111111111111111111111111111111111111a")
311 / 638859607902961221 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
351 / 638859607902961572 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
229 / 638859607902961801 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
194 / 638859607902961995 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
61 / 638859607902962056 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
77 / 638859607902962133 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
464 / 638859607902962597 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
136 / 638859607902962733 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
44 / 638859607902962777 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
540 / 638859607902963317 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
123 / 638859607902963440 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
320 / 638859607902963760 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
84 / 638859607902963844 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
89 / 638859607902963933 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
172 / 638859607902964105 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
117 / 638859607902964222 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
89 / 638859607902964311 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
828 / 638859607902965139 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
96 / 638859607902965235 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
51 / 638859607902965286 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
455 / 638859607902965741 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
59 / 638859607902965800 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
43 / 638859607902965843 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
299 / 638859607902966142 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
191 / 638859607902966333 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
43 / 638859607902966376 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
200 / 638859607902966576 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
161 / 638859607902966737 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
70 / 638859607902966807 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
41 / 638859607902966848 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
277 / 638859607902967125 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
39 / 638859607902967164 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
42 / 638859607902967206 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
299 / 638859607902967505 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
62 / 638859607902967567 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
67 / 638859607902967634 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
380 / 638859607902968014 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
257 / 638859607902968271 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
17578 / 638859607902985849 / Created
  ("file2.txt...22222222222222222222222222222222222222222222222a")
117 / 638859607902985966 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
36 / 638859607902986002 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
54 / 638859607902986056 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
43 / 638859607902986099 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
296 / 638859607902986395 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
53 / 638859607902986448 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
274 / 638859607902986722 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
57 / 638859607902986779 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
328 / 638859607902987107 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
68 / 638859607902987175 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
4358 / 638859607902991533 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
89 / 638859607902991622 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
51 / 638859607902991673 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
1004 / 638859607902992677 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
81 / 638859607902992758 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
1021 / 638859607902993779 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
84 / 638859607902993863 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
849 / 638859607902994712 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
82 / 638859607902994794 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
51 / 638859607902994845 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
394 / 638859607902995239 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
332 / 638859607902995571 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
43 / 638859607902995614 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
430 / 638859607902996044 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
56 / 638859607902996100 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
539 / 638859607902996639 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
61 / 638859607902996700 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
47 / 638859607902996747 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
1245 / 638859607902997992 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
292 / 638859607902998284 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
32 / 638859607902998316 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
909 / 638859607902999225 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
79 / 638859607902999304 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
40 / 638859607902999344 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
907 / 638859607903000251 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
68 / 638859607903000319 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
1109 / 638859607903001428 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
15021867 / 638859607918023295 / Changed
  ("file1....11111111111111111111111111111111111111111111111b")
287 / 638859607918023582 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
3518 / 638859607918027100 / Changed
  ("file1.txt"...11111111111111111111111111111111111111111111111b")
133 / 638859607918027233 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
67 / 638859607918027300 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
198 / 638859607918027498 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
230 / 638859607918027728 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
180 / 638859607918027908 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
71 / 638859607918027979 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
369 / 638859607918028348 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
212 / 638859607918028560 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
168 / 638859607918028728 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
152 / 638859607918028880 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
164 / 638859607918029044 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
157 / 638859607918029201 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
123 / 638859607918029324 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
69 / 638859607918029393 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
162 / 638859607918029555 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
191 / 638859607918029746 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
154 / 638859607918029900 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
170 / 638859607918030070 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
154 / 638859607918030224 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
170 / 638859607918030394 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
177 / 638859607918030571 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
159 / 638859607918030730 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
155 / 638859607918030885 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
162 / 638859607918031047 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
156 / 638859607918031203 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
159 / 638859607918031362 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
342 / 638859607918031704 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
345 / 638859607918032049 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
866 / 638859607918032915 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
73 / 638859607918032988 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
224 / 638859607918033212 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
192 / 638859607918033404 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
220 / 638859607918033624 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
191 / 638859607918033815 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
163 / 638859607918033978 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
50 / 638859607918034028 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
175 / 638859607918034203 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
52 / 638859607918034255 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
186 / 638859607918034441 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
50 / 638859607918034491 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
463 / 638859607918034954 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
133 / 638859607918035087 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
272 / 638859607918035359 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
63 / 638859607918035422 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
54 / 638859607918035476 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
478 / 638859607918035954 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
312 / 638859607918036266 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
56 / 638859607918036322 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
722 / 638859607918037044 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
78 / 638859607918037122 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
46 / 638859607918037168 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
234 / 638859607918037402 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
290 / 638859607918037692 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
43 / 638859607918037735 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
483 / 638859607918038218 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
65 / 638859607918038283 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
831 / 638859607918039114 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
74 / 638859607918039188 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
11548 / 638859607918050736 / Changed
  ("file1.txt...11111111111111111111111111111111111111111111111b")
110 / 638859607918050846 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
52 / 638859607918050898 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
46 / 638859607918050944 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
149 / 638859607918051093 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
206 / 638859607918051299 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
115 / 638859607918051414 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
250 / 638859607918051664 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
227 / 638859607918051891 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
204 / 638859607918052095 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
173 / 638859607918052268 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
119 / 638859607918052387 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
169 / 638859607918052556 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
117 / 638859607918052673 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
170 / 638859607918052843 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
114 / 638859607918052957 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
183 / 638859607918053140 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
211 / 638859607918053351 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
224 / 638859607918053575 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
52 / 638859607918053627 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
129 / 638859607918053756 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918053874 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
167 / 638859607918054041 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
106 / 638859607918054147 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
138 / 638859607918054285 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
123 / 638859607918054408 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
120 / 638859607918054528 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918054646 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
132 / 638859607918054778 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
108 / 638859607918054886 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
188 / 638859607918055074 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918055192 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
131 / 638859607918055323 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
203 / 638859607918055526 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
57 / 638859607918055583 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
170 / 638859607918055753 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
151 / 638859607918055904 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
211 / 638859607918056115 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
224 / 638859607918056339 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
259 / 638859607918056598 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
867 / 638859607918057465 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
79 / 638859607918057544 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
128 / 638859607918057672 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
188 / 638859607918057860 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918057978 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
318 / 638859607918058296 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
165 / 638859607918058461 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
138 / 638859607918058599 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
207 / 638859607918058806 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
213 / 638859607918059019 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
212 / 638859607918059231 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
120 / 638859607918059351 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
207 / 638859607918059558 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
54 / 638859607918059612 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
119 / 638859607918059731 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
149 / 638859607918059880 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
106 / 638859607918059986 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
189 / 638859607918060175 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
141 / 638859607918060316 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
124 / 638859607918060440 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918060558 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
118 / 638859607918060676 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
171 / 638859607918060847 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
121 / 638859607918060968 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
752 / 638859607918061720 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
67 / 638859607918061787 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
120 / 638859607918061907 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
197 / 638859607918062104 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
48 / 638859607918062152 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
180 / 638859607918062332 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
64 / 638859607918062396 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
122 / 638859607918062518 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
126 / 638859607918062644 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
164 / 638859607918062808 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
153 / 638859607918062961 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
136 / 638859607918063097 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
120 / 638859607918063217 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
116 / 638859607918063333 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
68 / 638859607918063401 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
237 / 638859607918063638 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
211 / 638859607918063849 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
175 / 638859607918064024 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
116 / 638859607918064140 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
168 / 638859607918064308 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
114 / 638859607918064422 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
193 / 638859607918064615 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
51 / 638859607918064666 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
126 / 638859607918064792 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
112 / 638859607918064904 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
172 / 638859607918065076 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
120 / 638859607918065196 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
163 / 638859607918065359 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
130 / 638859607918065489 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
204 / 638859607918065693 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
203 / 638859607918065896 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
191 / 638859607918066087 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
31 / 638859607918066118 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
45 / 638859607918066163 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
831 / 638859607918066994 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
15020086 / 638859607933087080 / Renamed
  ("file1....1111111111111111111111111111111111111111111111b"))
323 / 638859607933087403 / Renamed
  ("file2.txt",...2222222222222222222222222222222222222222222222b"))
15028449 / 638859607948115852 / Changed
  ("file_1...11111111111111111111111111111111111111111111111c")
216 / 638859607948116068 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
693 / 638859607948116761 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
5718 / 638859607948122479 / Changed
  ("file_1.txt...11111111111111111111111111111111111111111111111c")
106 / 638859607948122585 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
29 / 638859607948122614 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
237 / 638859607948122851 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
38 / 638859607948122889 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
394 / 638859607948123283 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
196 / 638859607948123479 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
28 / 638859607948123507 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
201 / 638859607948123708 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
31 / 638859607948123739 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
165 / 638859607948123904 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
33 / 638859607948123937 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
242 / 638859607948124179 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
120 / 638859607948124299 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
138 / 638859607948124437 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
153 / 638859607948124590 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
40 / 638859607948124630 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
158 / 638859607948124788 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
138 / 638859607948124926 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
129 / 638859607948125055 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
120 / 638859607948125175 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
153 / 638859607948125328 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
29 / 638859607948125357 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
561 / 638859607948125918 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
38 / 638859607948125956 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
194 / 638859607948126150 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
171 / 638859607948126321 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
145 / 638859607948126466 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
136 / 638859607948126602 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
138 / 638859607948126740 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
148 / 638859607948126888 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
149 / 638859607948127037 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
126 / 638859607948127163 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
120 / 638859607948127283 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
395 / 638859607948127678 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
148 / 638859607948127826 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
148 / 638859607948127974 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
358 / 638859607948128332 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
55 / 638859607948128387 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
155 / 638859607948128542 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
128 / 638859607948128670 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
140 / 638859607948128810 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
140 / 638859607948128950 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
135 / 638859607948129085 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
102 / 638859607948129187 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
126 / 638859607948129313 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
147 / 638859607948129460 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
173 / 638859607948129633 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
29 / 638859607948129662 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
135 / 638859607948129797 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
17642 / 638859607948147439 / Changed
  ("file_2.tx...22222222222222222222222222222222222222222222222c")
123 / 638859607948147562 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
379 / 638859607948147941 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
200 / 638859607948148141 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
337 / 638859607948148478 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
279 / 638859607948148757 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
130 / 638859607948148887 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
223 / 638859607948149110 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
207 / 638859607948149317 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
32 / 638859607948149349 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
216 / 638859607948149565 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
143 / 638859607948149708 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
31 / 638859607948149739 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
198 / 638859607948149937 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
367 / 638859607948150304 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
46 / 638859607948150350 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
157 / 638859607948150507 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
34 / 638859607948150541 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
168 / 638859607948150709 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
232 / 638859607948150941 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
143 / 638859607948151084 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
106 / 638859607948151190 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
837 / 638859607948152027 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
198 / 638859607948152225 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
137 / 638859607948152362 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
155 / 638859607948152517 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
147 / 638859607948152664 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
182 / 638859607948152846 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
142 / 638859607948152988 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
152 / 638859607948153140 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
155 / 638859607948153295 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
131 / 638859607948153426 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
135 / 638859607948153561 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
153 / 638859607948153714 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
138 / 638859607948153852 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
146 / 638859607948153998 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
139 / 638859607948154137 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
134 / 638859607948154271 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
150 / 638859607948154421 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
151 / 638859607948154572 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
136 / 638859607948154708 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
45 / 638859607948154753 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
162 / 638859607948154915 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
196 / 638859607948155111 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
155 / 638859607948155266 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
142 / 638859607948155408 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
133 / 638859607948155541 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
144 / 638859607948155685 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
109 / 638859607948155794 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
144 / 638859607948155938 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
138 / 638859607948156076 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
147 / 638859607948156223 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
1399 / 638859607948157622 / Changed
  ("file_2.txt...22222222222222222222222222222222222222222222222c")
174 / 638859607948157796 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
178 / 638859607948157974 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
297 / 638859607948158271 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
147 / 638859607948158418 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
162 / 638859607948158580 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
137 / 638859607948158717 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
152 / 638859607948158869 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
138 / 638859607948159007 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
129 / 638859607948159136 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
158 / 638859607948159294 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
161 / 638859607948159455 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
140 / 638859607948159595 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
140 / 638859607948159735 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
133 / 638859607948159868 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
158 / 638859607948160026 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
148 / 638859607948160174 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
140 / 638859607948160314 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
171 / 638859607948160485 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
140 / 638859607948160625 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
154 / 638859607948160779 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
135 / 638859607948160914 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
174 / 638859607948161088 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
153 / 638859607948161241 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
151 / 638859607948161392 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
156 / 638859607948161548 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
175 / 638859607948161723 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
36 / 638859607948161759 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
245 / 638859607948162004 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
162 / 638859607948162166 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
34 / 638859607948162200 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
192 / 638859607948162392 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
157 / 638859607948162549 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
145 / 638859607948162694 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
35 / 638859607948162729 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
205 / 638859607948162934 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
151 / 638859607948163085 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
32 / 638859607948163117 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
221 / 638859607948163338 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
123 / 638859607948163461 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
165 / 638859607948163626 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
47 / 638859607948163673 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
205 / 638859607948163878 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
154 / 638859607948164032 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
32 / 638859607948164064 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
174 / 638859607948164238 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
19 / 638859607948164257 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
173 / 638859607948164430 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
29 / 638859607948164459 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
868 / 638859607948165327 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
64 / 638859607948165391 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
15022000 / 638859607963187391 / Deleted "file_1.txt"
2693 / 638859607963190084 / Deleted "file_2.txt"
[Created ("file1.txt", Some "1a"); Changed ("file1.txt", Some "1a"); Created ("file2.txt", Some "2a");
 Changed ("file2.txt", Some "2a"); Changed ("file1.txt", Some "1b"); Changed ("file2.txt", Some "2b");
 Renamed ("file1.txt", ("file_1.txt", Some "1b")); Renamed ("file2.txt", ("file_2.txt", Some "2b"));
 Changed ("file_1.txt", Some "1c"); Changed ("file_2.txt", Some "2c"); Deleted "file_1.txt"; Deleted "file_2.txt"]

Some ()

testEventsSorted (test)¶

In [ ]:
//// test

let inline sortEvent event =
    match event with
    | FileSystemChange.Failure _ -> 0
    | FileSystemChange.Created _ -> 1
    | FileSystemChange.Changed _ -> 2
    | FileSystemChange.Renamed (_oldPath, _) -> 3
    | FileSystemChange.Deleted _ -> 4

let inline formatEvents events =
    events
    |> Seq.toList
    |> List.sortBy (snd >> sortEvent)
    |> List.choose (fun (ticks, event) ->
        match event with
        | FileSystemChange.Failure _ ->
            None
        | FileSystemChange.Changed (path, _) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Changed)
        | FileSystemChange.Created (path, _) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Created)
        | FileSystemChange.Deleted path ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Deleted)
        | FileSystemChange.Renamed (_oldPath, (path, _)) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Renamed)
    )
    |> List.sortBy (fun (_, path, _) -> path)
    |> List.distinctBy (fun (_, path, event) -> path, event)

let inline testEventsSorted
    (watchFn : string -> FSharp.Control.AsyncSeq<int64 * FileSystemChange> * IDisposable)
    write
    =
    let struct (tempDir, tempDisposable) =
        "FileSystem.testEventsSorted"
        |> SpiralCrypto.hash_text
        |> SpiralFileSystem.create_temp_dir'
    let stream, disposable = watchFn tempDir

    let events = System.Collections.Concurrent.ConcurrentBag ()

    let inline iter () =
        stream
        |> FSharp.Control.AsyncSeq.iterAsyncParallel (fun event -> async { events.Add event })

    let run = async {
        let! _ = iter () |> Async.StartChild
        do! Async.Sleep 250
        return! write tempDir
    }

    try
        run
        |> Async.runWithTimeout 5000
        |> _assertEqual (Some ())
    finally
        disposable.Dispose ()
        tempDisposable.Dispose ()

    let events = formatEvents events

    let eventMap =
        events
        |> List.map (fun (ticks, path, event) -> path, (event, ticks))
        |> List.groupBy fst
        |> List.map (fun (path, events) ->
            let event, _ticks =
                events
                |> List.map snd
                |> List.sortByDescending snd
                |> List.head

            path, event
        )
        |> Map.ofList

    let eventList =
        events
        |> List.map (fun (_ticks, path, event) -> path, event)

    eventMap, eventList

create and delete (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 3

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file1.txt", nameof FileSystemChangeType.Deleted

        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Deleted

        "file3.txt", nameof FileSystemChangeType.Created
        "file3.txt", nameof FileSystemChangeType.Changed
        "file3.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Deleted
        "file2.txt", nameof FileSystemChangeType.Deleted
        "file3.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:14 d #5 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file1.txt", "Deleted"); ("file2.txt", "Created");
 ("file2.txt", "Changed"); ("file2.txt", "Deleted"); ("file3.txt", "Created"); ("file3.txt", "Changed");
 ("file3.txt", "Deleted")]

map [("file1.txt", "Deleted"); ("file2.txt", "Deleted"); ("file3.txt", "Deleted")]

Some ()

change (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! "" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file1.txt", nameof FileSystemChangeType.Deleted

        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Deleted
        "file2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:16 d #6 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file1.txt", "Deleted"); ("file2.txt", "Created");
 ("file2.txt", "Changed"); ("file2.txt", "Deleted")]

map [("file1.txt", "Deleted"); ("file2.txt", "Deleted")]

Some ()

rename (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed

        "file_1.txt", nameof FileSystemChangeType.Renamed
        "file_1.txt", nameof FileSystemChangeType.Deleted

        "file_2.txt", nameof FileSystemChangeType.Renamed
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Deleted
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:17 d #7 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file2.txt", "Created"); ("file2.txt", "Changed");
 ("file_1.txt", "Renamed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Renamed"); ("file_2.txt", "Deleted")]

map [("file1.txt", "Changed"); ("file2.txt", "Changed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Deleted")]

Some ()

full (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! "" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed

        "file_1.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Renamed
        "file_1.txt", nameof FileSystemChangeType.Deleted

        "file_2.txt", nameof FileSystemChangeType.Changed
        "file_2.txt", nameof FileSystemChangeType.Renamed
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Deleted
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:18 d #8 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file2.txt", "Created"); ("file2.txt", "Changed");
 ("file_1.txt", "Changed"); ("file_1.txt", "Renamed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Changed");
 ("file_2.txt", "Renamed"); ("file_2.txt", "Deleted")]

map [("file1.txt", "Changed"); ("file2.txt", "Changed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Deleted")]

Some ()