diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 11ef6b7e..2ed8ad1d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -6,13 +6,15 @@ "version": "11.4.3", "commands": [ "fsdocs" - ] + ], + "rollForward": false }, "fable": { - "version": "3.4.2", + "version": "4.25.0", "commands": [ "fable" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 83559819..5bc667b0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,16 +8,16 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v4.3.1 with: - dotnet-version: '5.0.202' + dotnet-version: '8.0.x' - name: Install tools run: dotnet tool restore - - name: Build and Test + - name: Build and Test run: dotnet test -c Release - name: Pack run: dotnet pack -c Release - - name: Build docs + - name: Build docs run: dotnet fsdocs build --properties Configuration=Release - name: Deploy uses: peaceiris/actions-gh-pages@v3 @@ -30,5 +30,4 @@ jobs: run: dotnet nuget push src/FSharp.Control.AsyncSeq/bin/Release/*.nupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_ORG_TOKEN_2021 }} --skip-duplicate # NUGET_ORG_TOKEN_2021 is listed in "Repository secrets" in https://github.com/fsprojects/FSharp.Control.AsyncSeq/settings/secrets/actions -# note, the nuget org token expires around 24 July 2022 - +# note, the nuget org token expires around 24 July 2022 \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index d3d5b6b6..1413c30f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -9,17 +9,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Setup .NET - uses: actions/setup-dotnet@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v4.3.1 with: - dotnet-version: '5.0.202' + dotnet-version: '8.0.x' - name: Setup Node.js environment uses: actions/setup-node@v2.4.0 with: - node-version: 14.17.* + node-version: 14.17.* - name: Install tools run: dotnet tool restore - - name: Build and Test + - name: Build and Test run: dotnet test -c Release - name: Test Fable - run: cd tests/fable && npm i && npm test && cd ../.. + run: (cd tests/fable && npm i && npm test) \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..225f9f2b --- /dev/null +++ b/flake.lock @@ -0,0 +1,25 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1735563628, + "narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=", + "rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798", + "revCount": 637546, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.2405.637546%2Brev-b134951a4c9f3c995fd7be05f3243f8ecd65d798/01941dc2-2ab2-7453-8ebd-88712e28efae/source.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/NixOS/nixpkgs/0.2405.%2A.tar.gz" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..9d10015a --- /dev/null +++ b/flake.nix @@ -0,0 +1,38 @@ +{ + description = "FSharp.Control.AsyncSeq"; + + # Flake inputs + inputs = { + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2405.*.tar.gz"; + }; + + # Flake outputs + outputs = { self, nixpkgs }: + let + # Systems supported + allSystems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + + # Helper to provide system-specific attributes + forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f { + pkgs = import nixpkgs { inherit system; }; + }); + in + { + # Development environment output + devShells = forAllSystems ({ pkgs }: { + default = + + pkgs.mkShell { + packages = [ + # pkgs.bashInteractive + pkgs.dotnetCorePackages.sdk_8_0 + ]; + }; + }); + }; +} diff --git a/global.json b/global.json index 3a594dc3..fff00ca5 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "5.0.202", + "version": "8.0.19", "rollForward": "minor" } } diff --git a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqPerf.fsx b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqPerf.fsx index a4fa9b8f..dea0080f 100644 --- a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqPerf.fsx +++ b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqPerf.fsx @@ -1,4 +1,4 @@ -#r @"../../bin/FSharp.Control.AsyncSeq.dll" +#r @"../../src/FSharp.Control.AsyncSeq/bin/Release/netstandard2.1/FSharp.Control.AsyncSeq.dll" #nowarn "40" #time "on" @@ -107,7 +107,7 @@ let bindUnfold = -let collect n = +let collect n = AsyncSeq.replicate n () |> AsyncSeq.collect (fun () -> AsyncSeq.singleton ()) |> AsyncSeq.iter ignore @@ -122,7 +122,7 @@ let collect n = let Y = Choice1Of2 let S = Choice2Of2 - + let timeMs = 500 let inp0 = [ ] @@ -138,19 +138,18 @@ let toSeq (xs:Choice list) = asyncSeq { for x in xs do match x with | Choice1Of2 v -> yield v - | Choice2Of2 s -> do! Async.Sleep s } + | Choice2Of2 s -> do! Async.Sleep s } for (inp,exp) in [ (inp0,exp0) ; (inp1,exp1) ] do - let actual = + let actual = toSeq inp |> AsyncSeq.bufferByTime (timeMs - 5) |> AsyncSeq.map List.ofArray |> AsyncSeq.toListSynchronously - + printfn "actual=%A expected=%A" actual exp //let ls = toSeq inp |> AsyncSeq.toListSynchronously //let actualLs = actual |> List.concat - \ No newline at end of file diff --git a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs index ebbdd75d..9c209e48 100644 --- a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs +++ b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs @@ -1,5 +1,5 @@ #if INTERACTIVE -#load @"../../.paket/load/netcoreapp3.1/Test/NUnit.fsx" +#r "nuget: NUnit, 3.9.0" #time "on" #else @@ -10,7 +10,6 @@ open NUnit.Framework open FSharp.Control open System open System.Threading -open System.Threading.Tasks type AsyncOps = AsyncOps with static member unit : Async = async { return () } diff --git a/tests/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj b/tests/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj index 295fbabc..26eb01b0 100644 --- a/tests/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj +++ b/tests/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj @@ -1,6 +1,7 @@ - + + - net5.0 + net8.0 false @@ -13,4 +14,4 @@ - + \ No newline at end of file diff --git a/tests/fable/FSharp.Control.AsyncSeq.Tests/AsyncSeq.test.fs b/tests/fable/FSharp.Control.AsyncSeq.Tests/AsyncSeq.test.fs index ad087710..7993f865 100644 --- a/tests/fable/FSharp.Control.AsyncSeq.Tests/AsyncSeq.test.fs +++ b/tests/fable/FSharp.Control.AsyncSeq.Tests/AsyncSeq.test.fs @@ -41,9 +41,6 @@ let inline IsCancellationExn (e:exn) = let AreCancellationExns (e1:exn) (e2:exn) = IsCancellationExn e1 && IsCancellationExn e2 -let runTimeout (timeoutMs:int) (a:Async<'a>) : 'a = - Async.RunSynchronously (a, timeoutMs) - expect.extend("toEqualAsyncSeq", fun (actual: AsyncSeq) (expected: AsyncSeq) (timeout: int) (exnEq: exn -> exn -> bool) -> async { let! expected = expected |> AsyncSeq.toListAsync |> AsyncOps.timeoutMs timeout |> Async.Catch @@ -56,7 +53,7 @@ expect.extend("toEqualAsyncSeq", fun (actual: AsyncSeq) (expected: AsyncSeq | _ -> false |> fun b -> { pass = b; message = fun () -> sprintf "expected = %A actual = %A" expected actual } } |> Async.StartAsPromise) - + expect.extend("toEqualLooseChoice", fun (actual: Choice) (expected: Choice) -> match expected,actual with | Choice1Of2 exp, Choice1Of2 act -> exp = act @@ -98,11 +95,11 @@ type Jest with Jest.test("AsyncSeq.never should equal itself", async { let! n1 = AsyncSeq.never |> AsyncSeq.toListAsync |> Async.Catch let! n2 = AsyncSeq.never |> AsyncSeq.toListAsync |> Async.Catch - + Jest.expect(n1).toEqualLooseChoice(n2) }) -Jest.describe("AsyncSeq.concat", fun () -> +Jest.describe("AsyncSeq.concat", fun () -> Jest.test("AsyncSeq.concatSeq works", async { let ls = [ [1;2] ; [3;4] ] let actual = AsyncSeq.ofSeq ls |> AsyncSeq.concatSeq @@ -119,7 +116,7 @@ Jest.describe("AsyncSeq.concat", fun () -> |> AsyncSeq.ofSeq |> AsyncSeq.concat |> AsyncSeq.toArrayAsync - + let! expected = Seq.init m (fun _ -> Seq.init n id) |> AsyncSeq.ofSeq @@ -147,7 +144,7 @@ Jest.test("AsyncSeq.length works", async { Jest.expect(actual64 |> int).toEqual(expected) }) - + Jest.test("AsyncSeq.contains works", async { for i in 0 .. 10 do let ls = [ 1 .. i ] @@ -166,7 +163,7 @@ Jest.describe("AsyncSeq.pick", fun () -> let chooser x = if x = j then Some (string (x+1)) else None let! actual = AsyncSeq.ofSeq ls |> AsyncSeq.pick chooser |> Async.Catch let expected = (fun () -> ls |> Seq.pick chooser) |> catch - + do Jest.expect(actual).toEqualLooseChoice(expected) }) @@ -176,7 +173,7 @@ Jest.describe("AsyncSeq.pick", fun () -> for j in [0;i;i+1] do let actual = AsyncSeq.ofSeq ls |> AsyncSeq.tryPick (fun x -> if x = j then Some (string (x+1)) else None) let expected = ls |> Seq.tryPick (fun x -> if x = j then Some (string (x+1)) else None) - + do! Jest.expect(actual).toEqual(expected) }) ) @@ -211,7 +208,7 @@ Jest.test("AsyncSeq.forall works", async { do! Jest.expect(actual).toEqual(expected) }) -Jest.describe("AsyncSeq.unfold", fun () -> +Jest.describe("AsyncSeq.unfold", fun () -> Jest.test("AsyncSeq.unfoldAsync", async { let gen s = if s < 3 then (s,s + 1) |> Some @@ -221,7 +218,7 @@ Jest.describe("AsyncSeq.unfold", fun () -> do! Jest.expect(actual).toEqualAsyncSeq(expected) }) - + Jest.test("AsyncSeq.unfold", async { let gen s = if s < 3 then (s,s + 1) |> Some @@ -259,7 +256,7 @@ Jest.describe("AsyncSeq.unfold", fun () -> }) ) -Jest.describe("AsyncSeq.interleaveChoice", fun () -> +Jest.describe("AsyncSeq.interleaveChoice", fun () -> Jest.test("AsyncSeq.interleaveChoice", async { let s1 = AsyncSeq.ofSeq ["a";"b";"c"] let s2 = AsyncSeq.ofSeq [1;2;3] @@ -321,7 +318,7 @@ Jest.describe("AsyncSeq.interleaveChoice", fun () -> }) ) -Jest.describe("AsyncSeq.interleave", fun () -> +Jest.describe("AsyncSeq.interleave", fun () -> Jest.test("AsyncSeq.interleave", async { let s1 = AsyncSeq.ofSeq ["a";"b";"c"] let s2 = AsyncSeq.ofSeq ["1";"2";"3"] @@ -402,12 +399,12 @@ Jest.describe("AsyncSeq.interleave", fun () -> return! AsyncSeq.interleave s1 s2 |> AsyncSeq.toArrayAsync } - + do! Jest.expect(f |> Async.StartAsPromise).rejects.toThrow() }) ) -Jest.describe("AsyncSeq.bufferBy", fun () -> +Jest.describe("AsyncSeq.bufferBy", fun () -> Jest.test("AsyncSeq.bufferByCount", async { let! actual = asyncSeq { @@ -433,7 +430,7 @@ Jest.describe("AsyncSeq.bufferBy", fun () -> } |> AsyncSeq.bufferByCount 1 |> AsyncSeq.toArrayAsync let expected = [|for i in 1 .. sz -> [|i|]|] - + Jest.expect(actual).toEqual(expected) Jest.expect(actual).toHaveLength(expected.Length) }) @@ -469,7 +466,7 @@ Jest.describe("AsyncSeq.try", fun () -> Jest.expect(x.Value).toBe(6) }) - + Jest.test("try finally works exception", async { let x = ref 0 let s = @@ -586,7 +583,7 @@ Jest.describe("AsyncSeq.skip", fun () -> let p i = i <= 2 let! actual = ls |> AsyncSeq.ofSeq |> AsyncSeq.skipWhileAsync (p >> async.Return) |> AsyncSeq.toArrayAsync let! expected = ls |> Seq.skipWhile p |> AsyncSeq.ofSeq |> AsyncSeq.toArrayAsync - + Jest.expect(actual).toEqual(expected) }) @@ -604,14 +601,14 @@ Jest.describe("AsyncSeq.take", fun () -> let p i = i < 4 let! actual = ls |> AsyncSeq.ofSeq |> AsyncSeq.takeWhileAsync (p >> async.Return) |> AsyncSeq.toArrayAsync let! expected = ls |> Seq.takeWhile p |> AsyncSeq.ofSeq |> AsyncSeq.toArrayAsync - + Jest.expect(actual).toEqual(expected) }) Jest.test("AsyncSeq.take should work", async { let! sa = asyncSeq { yield ["a",1] |> Map.ofList } |> AsyncSeq.take 1 |> AsyncSeq.toArrayAsync let actual = sa |> (Array.tryHead >> Option.bind (Map.tryFind("a"))) - + Jest.expect(actual).toBeDefined() Jest.expect(actual.Value).toBe(1) }) @@ -622,7 +619,7 @@ Jest.describe("AsyncSeq.take", fun () -> let pInclusive i = i <= 4 let! actual = ls |> AsyncSeq.ofSeq |> AsyncSeq.takeWhileInclusive p |> AsyncSeq.toArrayAsync let! expected = ls |> Seq.filter(pInclusive) |> AsyncSeq.ofSeq |> AsyncSeq.toArrayAsync - + Jest.expect(actual).toEqual(expected) }) @@ -650,7 +647,7 @@ Jest.describe("AsyncSeq.scan", fun () -> let z = 0 let! actual = ls |> AsyncSeq.ofSeq |> AsyncSeq.scanAsync (fun i a -> f i a |> async.Return) z |> AsyncSeq.toArrayAsync let! expected = ls |> List.scan f z |> AsyncSeq.ofSeq |> AsyncSeq.toArrayAsync - + Jest.expect(actual).toEqual(expected) }) @@ -660,12 +657,12 @@ Jest.describe("AsyncSeq.scan", fun () -> let z = 0 let! actual = ls |> AsyncSeq.ofSeq |> AsyncSeq.scan (fun i a -> f i a) z |> AsyncSeq.toArrayAsync let! expected = ls |> List.scan f z |> AsyncSeq.ofSeq |> AsyncSeq.toArrayAsync - + Jest.expect(actual).toEqual(expected) }) ) -Jest.describe("AsyncSeq.fold", fun () -> +Jest.describe("AsyncSeq.fold", fun () -> Jest.test("AsyncSeq.foldAsync", async { for ls in [ []; [1]; [3]; [1;2;3;4;5] ] do let f i a = i + a @@ -675,7 +672,7 @@ Jest.describe("AsyncSeq.fold", fun () -> do! Jest.expect(actual).toBe(expected) }) - + Jest.test("AsyncSeq.fold", async { for ls in [ []; [1]; [3]; [1;2;3;4;5] ] do let f i a = i + a @@ -687,7 +684,7 @@ Jest.describe("AsyncSeq.fold", fun () -> }) ) -Jest.describe("AsyncSeq.filter", fun () -> +Jest.describe("AsyncSeq.filter", fun () -> Jest.test("AsyncSeq.filterAsync", async { for ls in [ []; [1]; [4]; [1;2;3;4;5] ] do let p i = i > 3 @@ -696,7 +693,7 @@ Jest.describe("AsyncSeq.filter", fun () -> Jest.expect(actual).toEqual(expected) }) - + Jest.test("AsyncSeq.filter", async { for ls in [ []; [1]; [4]; [1;2;3;4;5] ] do let p i = i > 3 @@ -707,7 +704,7 @@ Jest.describe("AsyncSeq.filter", fun () -> }) ) -Jest.describe("AsyncSeq.replicate", fun () -> +Jest.describe("AsyncSeq.replicate", fun () -> Jest.test("AsyncSeq.replicate", async { let c = 10 let x = "hello" @@ -756,7 +753,7 @@ Jest.describe("AsyncSeq.replicate", fun () -> }) ) -Jest.describe("AsyncSeq.init", fun () -> +Jest.describe("AsyncSeq.init", fun () -> Jest.test("AsyncSeq.init", async { for c in [0; 1; 100] do let! actual = AsyncSeq.init (int64 c) string |> AsyncSeq.toArrayAsync @@ -764,7 +761,7 @@ Jest.describe("AsyncSeq.init", fun () -> Jest.expect(actual).toEqual(expected) }) - + Jest.test("AsyncSeq.initInfinite", async { for c in [0; 1; 100] do let! actual = AsyncSeq.initInfinite string |> AsyncSeq.take c |> AsyncSeq.toArrayAsync @@ -786,7 +783,7 @@ Jest.describe("AsyncSeq.init", fun () -> Jest.expect(actual).toEqual(expected) }) - + Jest.test("AsyncSeq.initInfiniteAsync", async { for c in [0; 1; 100] do let! actual = AsyncSeq.initInfiniteAsync (string >> async.Return) |> AsyncSeq.take c |> AsyncSeq.toArrayAsync @@ -804,7 +801,7 @@ Jest.test("AsyncSeq.collect works", async { Jest.expect(actual).toEqual(expected) }) -Jest.describe("AsyncSeq.traverse", fun () -> +Jest.describe("AsyncSeq.traverse", fun () -> Jest.test("AsyncSeq.traverseOptionAsync", async { let seen = ResizeArray<_>() let s = [1;2;3;4;5] |> AsyncSeq.ofSeq @@ -870,12 +867,12 @@ Jest.test("AsyncSeq.getIterator should work", async { use i = s1.GetEnumerator() match! i.MoveNext() with - | None as v -> Jest.expect(v).toBeDefined() + | None as v -> Jest.expect(v).toBeDefined() | Some v -> Jest.expect(v).toBe(1) match! i.MoveNext() with - | None as v -> Jest.expect(v).toBeDefined() + | None as v -> Jest.expect(v).toBeDefined() | Some v -> Jest.expect(v).toBe(2) do! Jest.expect(i.MoveNext()).toBeUndefined() @@ -934,7 +931,7 @@ Jest.describe("AsyncSeq.intervalMs", fun () -> actual := (timestamp |> Array.map (fun d -> d.Ticks)) |> Array.append actual.Value } |> Async.StartImmediate - + for i in [0 .. 10] do Jest.expect(actual.Value).toHaveLength(i) diff --git a/tests/fable/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj b/tests/fable/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj index 420bdef0..cc1c6e05 100644 --- a/tests/fable/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj +++ b/tests/fable/FSharp.Control.AsyncSeq.Tests/FSharp.Control.AsyncSeq.Tests.fsproj @@ -1,4 +1,5 @@ - + + netstandard2.0 false