Skip to content

Commit 3b0db0a

Browse files
authored
Merge branch 'main' into repo-assist/test-choose-expand-2026-03-2bf13a937c3f20af
2 parents 01eb08e + 9096d43 commit 3b0db0a

File tree

7 files changed

+61
-15
lines changed

7 files changed

+61
-15
lines changed

.github/workflows/build.yaml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ jobs:
1515

1616
- name: setup-dotnet
1717
uses: actions/setup-dotnet@v4
18+
19+
- name: Cache NuGet packages
20+
uses: actions/cache@v4
21+
with:
22+
path: ~/.nuget/packages
23+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
24+
restore-keys: nuget-${{ runner.os }}-
1825

1926
- name: tool restore
2027
run: dotnet tool restore
@@ -36,6 +43,14 @@ jobs:
3643
- name: setup-dotnet
3744
uses: actions/setup-dotnet@v4
3845

46+
# cache NuGet packages to avoid re-downloading on every run
47+
- name: Cache NuGet packages
48+
uses: actions/cache@v4
49+
with:
50+
path: ~/.nuget/packages
51+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
52+
restore-keys: nuget-${{ runner.os }}-
53+
3954
# build it, test it, pack it
4055
- name: Run dotnet build (release)
4156
# see issue #105

.github/workflows/main.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ jobs:
1818
# setup dotnet based on global.json
1919
- name: setup-dotnet
2020
uses: actions/setup-dotnet@v4
21+
# cache NuGet packages to avoid re-downloading on every run
22+
- name: Cache NuGet packages
23+
uses: actions/cache@v4
24+
with:
25+
path: ~/.nuget/packages
26+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
27+
restore-keys: nuget-${{ runner.os }}-
2128
# build it, test it, pack it
2229
- name: Run dotnet build (release)
2330
# see issue #105
@@ -37,6 +44,13 @@ jobs:
3744
# setup dotnet based on global.json
3845
- name: setup-dotnet
3946
uses: actions/setup-dotnet@v4
47+
# cache NuGet packages to avoid re-downloading on every run
48+
- name: Cache NuGet packages
49+
uses: actions/cache@v4
50+
with:
51+
path: ~/.nuget/packages
52+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
53+
restore-keys: nuget-${{ runner.os }}-
4054
# build it, test it, pack it
4155
- name: Run dotnet test - release
4256
# see issue #105

.github/workflows/test.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ jobs:
1717
- name: setup-dotnet
1818
uses: actions/setup-dotnet@v4
1919

20+
# cache NuGet packages to avoid re-downloading on every run
21+
- name: Cache NuGet packages
22+
uses: actions/cache@v4
23+
with:
24+
path: ~/.nuget/packages
25+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
26+
restore-keys: nuget-${{ runner.os }}-
27+
2028
# build it, test it
2129
- name: Run dotnet test - release
2230
# see issue #105
@@ -47,6 +55,14 @@ jobs:
4755
- name: setup-dotnet
4856
uses: actions/setup-dotnet@v4
4957

58+
# cache NuGet packages to avoid re-downloading on every run
59+
- name: Cache NuGet packages
60+
uses: actions/cache@v4
61+
with:
62+
path: ~/.nuget/packages
63+
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.fsproj', '**/*.csproj', 'global.json') }}
64+
restore-keys: nuget-${{ runner.os }}-
65+
5066
# build it, test it
5167
- name: Run dotnet test - debug
5268
# see issue #105

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ FSharp.Control.TaskSeq is an F# library providing a `taskSeq` computation expres
77
## Repository Layout
88

99
- `src/FSharp.Control.TaskSeq/` — Main library (netstandard2.1)
10-
- `src/FSharp.Control.TaskSeq.Test/` — xUnit test project (net6.0)
10+
- `src/FSharp.Control.TaskSeq.Test/` — xUnit test project (net9.0)
1111
- `src/FSharp.Control.TaskSeq.SmokeTests/` — Smoke/integration tests
1212
- `src/FSharp.Control.TaskSeq.sln` — Solution file
1313
- `Version.props` — Single source of truth for the package version
@@ -97,7 +97,7 @@ All workflows are in `.github/workflows/`:
9797
- F# source files use `.fs` extension; signature files use `.fsi`.
9898
- `TreatWarningsAsErrors` is enabled for all projects.
9999
- File ordering matters in F# — the `<Compile>` order in `.fsproj` files defines compilation order.
100-
- The library targets `netstandard2.1`; tests target `net6.0` with `FSharp.Core` pinned to `6.0.1`.
100+
- The library targets `netstandard2.1`; tests target `net9.0` with `FSharp.Core` pinned to `6.0.1` (intentional: ensures compatibility with the minimum supported version).
101101
- NuGet packages are output to the `packages/` directory.
102102

103103
## Release Notes

src/FSharp.Control.TaskSeq.Test/TaskSeq.ExactlyOne.Tests.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ module Other =
104104
[<Fact>]
105105
let ``TaskSeq-exactlyOne gets the only item in a singleton sequence - variant`` () = task {
106106
let! exactlyOne =
107-
Gen.sideEffectTaskSeqMs 50<ms> 300<ms> 1
107+
Gen.sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 1
108108
|> TaskSeq.exactlyOne
109109

110110
exactlyOne |> should equal 1
@@ -113,7 +113,7 @@ module Other =
113113
[<Fact>]
114114
let ``TaskSeq-tryExactlyOne gets the only item in a singleton sequence - variant`` () = task {
115115
let! exactlyOne =
116-
Gen.sideEffectTaskSeqMs 50<ms> 300<ms> 1
116+
Gen.sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 1
117117
|> TaskSeq.tryExactlyOne
118118

119119
exactlyOne |> should be Some'

src/FSharp.Control.TaskSeq.Test/TaskSeq.IsEmpty.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module Immutable =
2828

2929
[<Fact>]
3030
let ``TaskSeq-isEmpty returns false for delayed singleton sequence`` () =
31-
Gen.sideEffectTaskSeqMs 200<ms> 400<ms> 3
31+
Gen.sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 3
3232
|> TaskSeq.isEmpty
3333
|> Task.map (should be False)
3434

src/FSharp.Control.TaskSeq.Test/TestUtils.fs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ type DummyTaskFactory(µsecMin: int64<µs>, µsecMax: int64<µs>) =
9898
/// Creates dummy tasks with a randomized delay and a mutable state,
9999
/// to ensure we properly test whether processing is done ordered or not.
100100
/// Uses the defaults for <paramref name="µsecMin" /> and <paramref name="µsecMax" />
101-
/// with 10,000µs and 30,000µs respectively (or 10ms and 30ms).
101+
/// with 1,000µs and 5,000µs respectively (or 1ms and 5ms).
102102
/// </summary>
103-
new() = DummyTaskFactory(10_000L<µs>, 30_000L<µs>)
103+
new() = DummyTaskFactory(1_000L<µs>, 5_000L<µs>)
104104

105105
/// <summary>
106106
/// Creates dummy tasks with a randomized delay and a mutable state,
@@ -164,8 +164,9 @@ module TestUtils =
164164
>> TaskSeq.toArrayAsync
165165
>> Task.map (String >> should equal expected)
166166

167-
/// Delays (no spin-wait!) between 20 and 70ms, assuming a 15.6ms resolution clock
168-
let longDelay () = task { do! Task.Delay(Random().Next(20, 70)) }
167+
/// Waits using a real timer-based async delay (not spin-wait), causing an OS-level async yield point.
168+
/// On Windows, Task.Delay has ~15ms timer resolution, so this actually waits ~15ms.
169+
let longDelay () = task { do! Task.Delay 1 }
169170

170171
/// Spin-waits, occasionally normal delay, between 50µs - 18,000µs
171172
let microDelay () = task { do! DelayHelper.delayTask 50L<µs> 18_000L<µs> (fun _ -> ()) }
@@ -324,8 +325,8 @@ module TestUtils =
324325
yield x
325326
}
326327

327-
/// Create a bunch of dummy tasks, each lasting between 10-30ms with spin-wait delays.
328-
let sideEffectTaskSeq = sideEffectTaskSeqMicro 10_000L<µs> 30_000L<µs>
328+
/// Create a bunch of dummy tasks, each lasting between 1-5ms with spin-wait delays.
329+
let sideEffectTaskSeq = sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs>
329330

330331
/// Returns any of a set of variants that each create an empty sequence in a creative way.
331332
/// Please extend this with more cases.
@@ -428,7 +429,7 @@ module TestUtils =
428429
| SeqImmutable.AsyncYielded ->
429430
// by returning the 'side effect seq' from the closure of the CE,
430431
// the side-effect will NOT execute again
431-
taskSeq { yield! sideEffectTaskSeqMicro 15_000L<µs> 50_000L<µs> 10 }
432+
taskSeq { yield! sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 10 }
432433
| SeqImmutable.AsyncYielded_Nested ->
433434
// let's deeply nest the sequence, which should not cause extra side effects being executed.
434435
taskSeq {
@@ -442,7 +443,7 @@ module TestUtils =
442443
yield! taskSeq {
443444
// by returning the 'side effect seq' from the closure of the CE,
444445
// the side-effect will NOT execute again
445-
yield! sideEffectTaskSeqMicro 15_000L<µs> 50_000L<µs> 10
446+
yield! sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 10
446447
}
447448
}
448449
}
@@ -520,12 +521,12 @@ module TestUtils =
520521

521522
// delay just enough with a spin-wait to occasionally cause a thread-yield
522523
| SeqWithSideEffect.ThreadSpinWait -> sideEffectTaskSeqMicro 50L<µs> 5_000L<µs> 10
523-
| SeqWithSideEffect.AsyncYielded -> sideEffectTaskSeqMicro 15_000L<µs> 50_000L<µs> 10
524+
| SeqWithSideEffect.AsyncYielded -> sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 10
524525
| SeqWithSideEffect.AsyncYielded_Nested ->
525526
// let's deeply nest the sequence, which should not cause extra side effects being executed.
526527
// NOTE: this list of tasks must be defined OUTSIDE the scope, otherwise, mutability on 2nd
527528
// iteration will not kick in!
528-
let nestedTaskSeq = sideEffectTaskSeqMicro 15_000L<µs> 50_000L<µs> 10
529+
let nestedTaskSeq = sideEffectTaskSeqMicro 1_000L<µs> 5_000L<µs> 10
529530

530531
taskSeq {
531532
yield! taskSeq {

0 commit comments

Comments
 (0)