Compare commits

...

31 Commits

Author SHA1 Message Date
Patrick Stevens
81aa6832d5 Switch to trusted publishing (#301) 2025-10-03 09:37:51 +00:00
dependabot[bot]
a20f32de02 Bump WoofWare.Myriad.Plugins from 8.0.5 to 8.1.1 (#297)
* Bump WoofWare.Myriad.Plugins from 8.0.5 to 8.1.1

---
updated-dependencies:
- dependency-name: WoofWare.Myriad.Plugins
  dependency-version: 8.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-09-30 22:55:29 +01:00
dependabot[bot]
1e53e72d4a Bump ApiSurface from 5.0.1 to 5.0.2 (#299)
* Bump ApiSurface from 5.0.1 to 5.0.2

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-version: 5.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-09-30 22:49:22 +01:00
dependabot[bot]
b38a3fcc02 Upgrade to net9, bump Nerdbank.GitVersioning from 3.8.38-alpha to 3.8.118 (#300)
* Bump Nerdbank.GitVersioning from 3.8.38-alpha to 3.8.118

---
updated-dependencies:
- dependency-name: Nerdbank.GitVersioning
  dependency-version: 3.8.118
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

* Try upgrading to net9

* Bump tests

* Upgrade pipeline

* Bump version

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-09-30 07:21:29 +00:00
patrick-conscriptus[bot]
73dc21e11f Automated commit (#296)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-21 00:46:53 +00:00
patrick-conscriptus[bot]
5b54bb256e Automated commit (#295)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-14 00:45:51 +00:00
Patrick Stevens
b56e1b1542 Bump ApiSurface (#294) 2025-09-08 20:38:52 +00:00
dependabot[bot]
ba46b1edb6 Bump Spectre.Console from 0.50.0 to 0.51.1 (#293)
* Bump Spectre.Console from 0.50.0 to 0.51.1

---
updated-dependencies:
- dependency-name: Spectre.Console
  dependency-version: 0.51.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-09-08 17:41:56 +00:00
patrick-conscriptus[bot]
72674e1711 Automated commit (#292)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-09-07 00:46:50 +00:00
dependabot[bot]
c4b862bdd8 Bump actions/attest-build-provenance from 2.4.0 to 3.0.0 (#291) 2025-09-02 07:29:12 +01:00
patrick-conscriptus[bot]
4c629b1d64 Automated commit (#290)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-31 00:47:27 +00:00
dependabot[bot]
e67820c56d Bump FsCheck from 3.3.0 to 3.3.1 (#289)
* Bump FsCheck from 3.3.0 to 3.3.1

---
updated-dependencies:
- dependency-name: FsCheck
  dependency-version: 3.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-08-27 08:59:47 +00:00
patrick-conscriptus[bot]
31bff4cb03 Automated commit (#288)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-24 00:50:07 +00:00
patrick-conscriptus[bot]
d081cfaafb Automated commit (#287)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-17 00:52:15 +00:00
dependabot[bot]
c237df3885 Bump actions/checkout from 4 to 5 (#286) 2025-08-12 07:31:15 +01:00
dependabot[bot]
3b9b9eb4c8 Bump actions/download-artifact from 4 to 5 (#285) 2025-08-11 23:30:06 +01:00
patrick-conscriptus[bot]
12e3fc0e4f Automated commit (#284)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-10 00:55:19 +00:00
dependabot[bot]
208b809096 Bump fsharp-analyzers from 0.32.0 to 0.32.1 (#282)
* Bump fsharp-analyzers from 0.32.0 to 0.32.1

---
updated-dependencies:
- dependency-name: fsharp-analyzers
  dependency-version: 0.32.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-08-05 19:07:01 +00:00
dependabot[bot]
b4e5baddcf Bump NasAmin/trx-parser from 0.6.0 to 0.7.0 (#283)
* Bump NasAmin/trx-parser from 0.6.0 to 0.7.0

Bumps [NasAmin/trx-parser](https://github.com/nasamin/trx-parser) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/nasamin/trx-parser/releases)
- [Changelog](https://github.com/NasAmin/trx-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nasamin/trx-parser/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: NasAmin/trx-parser
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Don't run external dependency on main

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-08-05 19:59:56 +01:00
patrick-conscriptus[bot]
5597b3f2f8 Automated commit (#281)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-08-03 00:57:08 +00:00
Patrick Stevens
fcfdcef6cf Async tests (#280) 2025-07-29 23:07:12 +00:00
Patrick Stevens
eeada219f6 Permit async ParallelQueue (#279) 2025-07-29 22:24:58 +01:00
Patrick Stevens
99e0fdff08 Make ParallelQueue surface its errors, correctly flow ExecutionContext (#278) 2025-07-29 22:04:45 +01:00
dependabot[bot]
fda4e7ba60 Bump WoofWare.Myriad.Plugins from 8.0.4 to 8.0.5 (#277)
* Bump WoofWare.Myriad.Plugins from 8.0.4 to 8.0.5

---
updated-dependencies:
- dependency-name: WoofWare.Myriad.Plugins
  dependency-version: 8.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-07-28 20:42:53 +01:00
patrick-conscriptus[bot]
dfdfa84733 Automated commit (#276)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-27 00:56:11 +00:00
patrick-conscriptus[bot]
c218110749 Automated commit (#275)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-20 00:55:52 +00:00
dependabot[bot]
309968721c Bump ApiSurface from 4.1.21 to 4.1.22 (#274)
* Bump ApiSurface from 4.1.21 to 4.1.22

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-version: 4.1.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-07-14 21:03:17 +00:00
patrick-conscriptus[bot]
876ca9e625 Automated commit (#273)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-13 00:54:57 +00:00
patrick-conscriptus[bot]
59f9789cdc Automated commit (#271)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-07-06 00:53:02 +00:00
dependabot[bot]
c8c28b9a32 Bump FsUnit to 7.1.1 (#270)
* Bump FsUnit to 7.1.1

---
updated-dependencies:
- dependency-name: FsUnit
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: FsUnit
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: FsUnit
  dependency-version: 7.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-06-30 20:56:14 +00:00
patrick-conscriptus[bot]
6d87610017 Automated commit (#269)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-06-29 00:53:55 +00:00
24 changed files with 798 additions and 242 deletions

View File

@@ -3,13 +3,13 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"fantomas": { "fantomas": {
"version": "7.0.2", "version": "7.0.3",
"commands": [ "commands": [
"fantomas" "fantomas"
] ]
}, },
"fsharp-analyzers": { "fsharp-analyzers": {
"version": "0.31.0", "version": "0.32.1",
"commands": [ "commands": [
"fsharp-analyzers" "fsharp-analyzers"
] ]

22
.envrc
View File

@@ -1 +1,23 @@
use flake use flake
DOTNET_PATH=$(readlink "$(which dotnet)")
SETTINGS_FILE=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user')
MSBUILD=$(realpath "$(find "$(dirname "$DOTNET_PATH")/../share/dotnet/sdk" -maxdepth 2 -type f -name MSBuild.dll)")
if [ -f "$SETTINGS_FILE" ] ; then
xmlstarlet ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \
--value "$(realpath "$(dirname "$DOTNET_PATH")/../share/dotnet/dotnet")" \
"$SETTINGS_FILE"
xmlstarlet ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \
--value "$MSBUILD" \
"$SETTINGS_FILE"
fi

View File

@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
@@ -39,12 +39,12 @@ jobs:
run: 'nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}' run: 'nix develop --command dotnet build --no-restore --configuration ${{matrix.config}}'
- name: Test - name: Test
run: | run: |
nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}} --framework net8.0 --filter 'FullyQualifiedName !~ FailingConsumer' nix develop --command dotnet test --no-build --verbosity normal --configuration ${{matrix.config}} --filter 'FullyQualifiedName !~ FailingConsumer'
selftest-intended-failures: selftest-intended-failures:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
@@ -57,7 +57,7 @@ jobs:
- name: Build - name: Build
run: 'nix develop --command dotnet build --no-restore --configuration Release' run: 'nix develop --command dotnet build --no-restore --configuration Release'
- name: Test using self - name: Test using self
run: 'nix develop --command dotnet exec ./WoofWare.NUnitTestRunner/bin/Release/net6.0/WoofWare.NUnitTestRunner.dll ./FailingConsumer/bin/Release/net8.0/FailingConsumer.dll --trx TrxOut/out.trx || true' run: 'nix develop --command dotnet exec ./WoofWare.NUnitTestRunner/bin/Release/net9.0/WoofWare.NUnitTestRunner.dll ./FailingConsumer/bin/Release/net9.0/FailingConsumer.dll --trx TrxOut/out.trx || true'
- name: Munge output - name: Munge output
run: 'nix develop --command xmlstarlet sel -N x="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" -t -m "//x:UnitTestResult" -v "@testName" -o ": " -v ".//x:ErrorInfo/x:Message" -n TrxOut/out.trx > snapshot.txt' run: 'nix develop --command xmlstarlet sel -N x="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" -t -m "//x:UnitTestResult" -v "@testName" -o ": " -v ".//x:ErrorInfo/x:Message" -n TrxOut/out.trx > snapshot.txt'
- name: Check output matches expected - name: Check output matches expected
@@ -81,7 +81,7 @@ jobs:
statuses: read statuses: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
@@ -94,10 +94,10 @@ jobs:
- name: Build - name: Build
run: 'nix develop --command dotnet build --no-restore --configuration Release' run: 'nix develop --command dotnet build --no-restore --configuration Release'
- name: Test using self - name: Test using self
run: 'nix develop --command dotnet exec ./WoofWare.NUnitTestRunner/bin/Release/net6.0/WoofWare.NUnitTestRunner.dll ./Consumer/bin/Release/net8.0/Consumer.dll --trx TrxOut/out.trx' run: 'nix develop --command dotnet exec ./WoofWare.NUnitTestRunner/bin/Release/net9.0/WoofWare.NUnitTestRunner.dll ./Consumer/bin/Release/net9.0/Consumer.dll --trx TrxOut/out.trx'
- name: Parse Trx files - name: Parse Trx files
uses: NasAmin/trx-parser@v0.6.0 uses: NasAmin/trx-parser@v0.7.0
if: always() if: always() && github.ref_name != 'main'
id: trx-parser id: trx-parser
with: with:
TRX_PATH: ${{ github.workspace }}/TrxOut TRX_PATH: ${{ github.workspace }}/TrxOut
@@ -109,7 +109,7 @@ jobs:
security-events: write security-events: write
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
@@ -128,7 +128,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
with: with:
@@ -143,7 +143,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
with: with:
@@ -156,7 +156,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
with: with:
@@ -194,7 +194,7 @@ jobs:
nuget-pack: nuget-pack:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
@@ -224,7 +224,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Download NuGet artifact (lib) - name: Download NuGet artifact (lib)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-lib name: nuget-package-lib
path: packed-lib path: packed-lib
@@ -232,7 +232,7 @@ jobs:
# Verify that there is exactly one nupkg in the artifact that would be NuGet published # Verify that there is exactly one nupkg in the artifact that would be NuGet published
run: if [[ $(find packed-lib -maxdepth 1 -name 'WoofWare.NUnitTestRunner.Lib.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi run: if [[ $(find packed-lib -maxdepth 1 -name 'WoofWare.NUnitTestRunner.Lib.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
- name: Download NuGet artifact (tool) - name: Download NuGet artifact (tool)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-tool name: nuget-package-tool
path: packed-tool path: packed-tool
@@ -259,12 +259,12 @@ jobs:
contents: read contents: read
steps: steps:
- name: Download NuGet artifact - name: Download NuGet artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-lib name: nuget-package-lib
path: packed path: packed
- name: Attest Build Provenance - name: Attest Build Provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0 uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with: with:
subject-path: "packed/*.nupkg" subject-path: "packed/*.nupkg"
@@ -278,12 +278,12 @@ jobs:
contents: read contents: read
steps: steps:
- name: Download NuGet artifact - name: Download NuGet artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-tool name: nuget-package-tool
path: packed path: packed
- name: Attest Build Provenance - name: Attest Build Provenance
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0 uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
with: with:
subject-path: "packed/*.nupkg" subject-path: "packed/*.nupkg"
@@ -297,25 +297,30 @@ jobs:
attestations: write attestations: write
contents: read contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact - name: Download NuGet artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-lib name: nuget-package-lib
path: packed path: packed
- name: Identify .NET - name: Identify .NET
id: identify-dotnet id: identify-dotnet
run: nix develop --command bash -c "echo dotnet=$(which dotnet) >> $GITHUB_OUTPUT" run: nix develop --command bash -c "echo dotnet=$(which dotnet) >> $GITHUB_OUTPUT"
- name: Obtain NuGet key
uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544
id: login
with:
user: ${{ secrets.NUGET_USER }}
- name: Publish NuGet package - name: Publish NuGet package
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059 uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
with: with:
package-name: WoofWare.NUnitTestRunner.Lib package-name: WoofWare.NUnitTestRunner.Lib
nuget-key: ${{ secrets.NUGET_API_KEY }} nuget-key: ${{ steps.login.outputs.NUGET_API_KEY }}
nupkg-dir: packed/ nupkg-dir: packed/
dotnet: ${{ steps.identify-dotnet.outputs.dotnet }} dotnet: ${{ steps.identify-dotnet.outputs.dotnet }}
@@ -329,25 +334,30 @@ jobs:
attestations: write attestations: write
contents: read contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v31 uses: cachix/install-nix-action@v31
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact - name: Download NuGet artifact
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: nuget-package-tool name: nuget-package-tool
path: packed path: packed
- name: Identify .NET - name: Identify .NET
id: identify-dotnet id: identify-dotnet
run: nix develop --command bash -c "echo dotnet=$(which dotnet) >> $GITHUB_OUTPUT" run: nix develop --command bash -c "echo dotnet=$(which dotnet) >> $GITHUB_OUTPUT"
- name: Obtain NuGet key
uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544
id: login
with:
user: ${{ secrets.NUGET_USER }}
- name: Publish NuGet package - name: Publish NuGet package
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059 uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
with: with:
package-name: WoofWare.NUnitTestRunner package-name: WoofWare.NUnitTestRunner
nuget-key: ${{ secrets.NUGET_API_KEY }} nuget-key: ${{ steps.login.outputs.NUGET_API_KEY }}
nupkg-dir: packed/ nupkg-dir: packed/
dotnet: ${{ steps.identify-dotnet.outputs.dotnet }} dotnet: ${{ steps.identify-dotnet.outputs.dotnet }}
@@ -360,9 +370,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [nuget-pack] needs: [nuget-pack]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Download NuGet artifact (tool) - name: Download NuGet artifact (tool)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: ${{ matrix.artifact }} name: ${{ matrix.artifact }}
- name: Compute package path - name: Compute package path
@@ -396,9 +406,9 @@ jobs:
permissions: permissions:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- name: Download NuGet artifact (tool) - name: Download NuGet artifact (tool)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v5
with: with:
name: ${{ matrix.artifact }} name: ${{ matrix.artifact }}
- name: Compute package path - name: Compute package path

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check out repository - name: Check out repository
uses: actions/checkout@v4 uses: actions/checkout@v5
- name: Install Nix - name: Install Nix
uses: DeterminateSystems/nix-installer-action@main uses: DeterminateSystems/nix-installer-action@main

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks> <TargetFrameworks>net9.0</TargetFrameworks>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
</PropertyGroup> </PropertyGroup>
@@ -10,6 +10,7 @@
<Compile Include="NoAttribute.fs" /> <Compile Include="NoAttribute.fs" />
<Compile Include="Inconclusive.fs" /> <Compile Include="Inconclusive.fs" />
<Compile Include="RunSubProcess.fs" /> <Compile Include="RunSubProcess.fs" />
<Compile Include="TestAsync.fs" />
<Compile Include="TestExplicit.fs" /> <Compile Include="TestExplicit.fs" />
<Compile Include="TestNonParallel.fs" /> <Compile Include="TestNonParallel.fs" />
<Compile Include="TestParallel.fs" /> <Compile Include="TestParallel.fs" />
@@ -27,7 +28,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FsUnit" Version="7.0.1" /> <PackageReference Include="FsUnit" Version="7.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageReference Include="NUnit" Version="4.3.2"/> <PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/> <PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>

23
Consumer/TestAsync.fs Normal file
View File

@@ -0,0 +1,23 @@
namespace Consumer
open System
open System.Threading.Tasks
open FsUnitTyped
open NUnit.Framework
[<TestFixture>]
module TestAsync =
[<Test>]
let ``an async test`` () =
async {
do! Async.Sleep (TimeSpan.FromMilliseconds 20.0)
1 |> shouldEqual 1
}
[<Test>]
let ``an async test, task-based`` () =
task {
do! Task.Delay (TimeSpan.FromMilliseconds 20.0)
1 |> shouldEqual 1
}

View File

@@ -10,7 +10,7 @@
<WarnOn>FS3388,FS3559</WarnOn> <WarnOn>FS3388,FS3559</WarnOn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.8.38-alpha" PrivateAssets="all"/> <PackageReference Include="Nerdbank.GitVersioning" Version="3.8.118" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(GITHUB_ACTION)' != ''"> <PropertyGroup Condition="'$(GITHUB_ACTION)' != ''">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
</PropertyGroup> </PropertyGroup>
@@ -11,7 +11,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FsUnit" Version="7.0.1" /> <PackageReference Include="FsUnit" Version="7.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
<PackageReference Include="NUnit" Version="4.3.2"/> <PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/> <PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>

View File

@@ -13,3 +13,7 @@ However, we would recommend phrasing some of them differently, for maximum peace
WoofWare.NUnitTestRunner has *limited* support for parallelism. WoofWare.NUnitTestRunner has *limited* support for parallelism.
By default, we run tests in parallel, taking half the available processors; we may or may not respect the NUnit parallelism attributes to any given extent that they tell us to be *more* parallel (but we will never incorrectly run tests in parallel). By default, we run tests in parallel, taking half the available processors; we may or may not respect the NUnit parallelism attributes to any given extent that they tell us to be *more* parallel (but we will never incorrectly run tests in parallel).
# Licence
WoofWare.NUnitTestRunner is licensed to you under the MIT licence, a copy of which can be found at [LICENSE](./LICENSE).

View File

@@ -10,20 +10,14 @@ open System.Threading
type internal OutputStreamId = | OutputStreamId of Guid type internal OutputStreamId = | OutputStreamId of Guid
type private ThreadAwareWriter type private ThreadAwareWriter (local : AsyncLocal<OutputStreamId>, underlying : Dictionary<OutputStreamId, TextWriter>)
(
local : AsyncLocal<OutputStreamId>,
underlying : Dictionary<OutputStreamId, TextWriter>,
mem : Dictionary<OutputStreamId, MemoryStream>
)
= =
inherit TextWriter () inherit TextWriter ()
override _.get_Encoding () = Encoding.Default override _.get_Encoding () = Encoding.Default
override this.Write (v : char) : unit = override this.Write (v : char) : unit =
use prev = ExecutionContext.Capture () lock
underlying
(fun _ ->
(fun () -> (fun () ->
match underlying.TryGetValue local.Value with match underlying.TryGetValue local.Value with
| true, output -> output.Write v | true, output -> output.Write v
@@ -31,16 +25,12 @@ type private ThreadAwareWriter
let wanted = let wanted =
underlying |> Seq.map (fun (KeyValue (a, b)) -> $"%O{a}") |> String.concat "\n" underlying |> Seq.map (fun (KeyValue (a, b)) -> $"%O{a}") |> String.concat "\n"
failwith $"no such context: %O{local.Value}\nwanted:\n" failwith $"no such context: %O{local.Value}\nwanted:\n{wanted}"
) )
|> lock underlying
)
|> fun action -> ExecutionContext.Run (prev, action, ())
override this.WriteLine (v : string) : unit = override this.WriteLine (v : string) : unit =
use prev = ExecutionContext.Capture () lock
underlying
(fun _ ->
(fun () -> (fun () ->
match underlying.TryGetValue local.Value with match underlying.TryGetValue local.Value with
| true, output -> output.WriteLine v | true, output -> output.WriteLine v
@@ -48,16 +38,13 @@ type private ThreadAwareWriter
let wanted = let wanted =
underlying |> Seq.map (fun (KeyValue (a, b)) -> $"%O{a}") |> String.concat "\n" underlying |> Seq.map (fun (KeyValue (a, b)) -> $"%O{a}") |> String.concat "\n"
failwith $"no such context: %O{local.Value}\nwanted:\n" failwith $"no such context: %O{local.Value}\nwanted:\n{wanted}"
) )
|> lock underlying
)
|> fun action -> ExecutionContext.Run (prev, action, ())
/// Wraps up the necessary context to intercept global state. /// Wraps up the necessary context to intercept global state.
[<NoEquality ; NoComparison>] [<NoEquality ; NoComparison>]
type TestContexts = type TestContexts =
private internal
{ {
/// Accesses to this must be locked on StdOutWriters. /// Accesses to this must be locked on StdOutWriters.
StdOuts : Dictionary<OutputStreamId, MemoryStream> StdOuts : Dictionary<OutputStreamId, MemoryStream>
@@ -77,8 +64,8 @@ type TestContexts =
let stdoutWriters = Dictionary () let stdoutWriters = Dictionary ()
let stderrWriters = Dictionary () let stderrWriters = Dictionary ()
let local = AsyncLocal () let local = AsyncLocal ()
let stdoutWriter = new ThreadAwareWriter (local, stdoutWriters, stdouts) let stdoutWriter = new ThreadAwareWriter (local, stdoutWriters)
let stderrWriter = new ThreadAwareWriter (local, stderrWriters, stderrs) let stderrWriter = new ThreadAwareWriter (local, stderrWriters)
{ {
StdOuts = stdouts StdOuts = stdouts

View File

@@ -0,0 +1,10 @@
namespace WoofWare.NUnitTestRunner
open System.Runtime.ExceptionServices
[<RequireQualifiedAccess>]
module internal Exception =
let reraiseWithOriginalStackTrace<'a> (e : exn) : 'a =
let edi = ExceptionDispatchInfo.Capture e
edi.Throw ()
failwith "unreachable"

View File

@@ -4,16 +4,16 @@ open System
open System.Threading open System.Threading
open System.Threading.Tasks open System.Threading.Tasks
type private ThunkEvaluator<'ret> = type private AsyncThunkEvaluator<'ret> =
abstract Eval<'a> : (unit -> 'a) -> AsyncReplyChannel<'a> -> 'ret abstract Eval<'a> : (unit -> Async<'a>) -> AsyncReplyChannel<Result<'a, exn>> -> 'ret
type private ThunkCrate = type private AsyncThunkCrate =
abstract Apply<'ret> : ThunkEvaluator<'ret> -> 'ret abstract Apply<'ret> : AsyncThunkEvaluator<'ret> -> 'ret
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module private ThunkCrate = module private AsyncThunkCrate =
let make<'a> (t : unit -> 'a) (rc : AsyncReplyChannel<'a>) : ThunkCrate = let make<'a> (t : unit -> Async<'a>) (rc : AsyncReplyChannel<Result<'a, exn>>) : AsyncThunkCrate =
{ new ThunkCrate with { new AsyncThunkCrate with
member _.Apply e = e.Eval t rc member _.Apply e = e.Eval t rc
} }
@@ -41,7 +41,11 @@ type private MailboxMessage =
| Quit of AsyncReplyChannel<unit> | Quit of AsyncReplyChannel<unit>
/// Check current state, see if we need to start more tests, etc. /// Check current state, see if we need to start more tests, etc.
| Reconcile | Reconcile
| RunTest of within : TestFixture * Parallelizable<unit> option * test : ThunkCrate | RunTestAsync of
within : TestFixture *
Parallelizable<unit> option *
test : AsyncThunkCrate *
context : ExecutionContext
| BeginTestFixture of TestFixture * AsyncReplyChannel<TestFixtureRunningToken> | BeginTestFixture of TestFixture * AsyncReplyChannel<TestFixtureRunningToken>
| EndTestFixture of TestFixtureTearDownToken * AsyncReplyChannel<unit> | EndTestFixture of TestFixtureTearDownToken * AsyncReplyChannel<unit>
@@ -310,21 +314,31 @@ type ParallelQueue
rc.Reply () rc.Reply ()
m.Post MailboxMessage.Reconcile m.Post MailboxMessage.Reconcile
return! processTask (Running state) m return! processTask (Running state) m
| MailboxMessage.RunTest (withinFixture, par, message) -> | MailboxMessage.RunTestAsync (withinFixture, par, message, capturedContext) ->
let t () = let t () =
{ new ThunkEvaluator<_> with { new AsyncThunkEvaluator<_> with
member _.Eval<'b> (t : unit -> 'b) rc = member _.Eval<'b> (t : unit -> Async<'b>) rc =
let tcs = TaskCompletionSource TaskCreationOptions.RunContinuationsAsynchronously let tcs = TaskCompletionSource TaskCreationOptions.RunContinuationsAsynchronously
use ec = ExecutionContext.Capture ()
fun () -> fun () ->
ExecutionContext.Run ( ExecutionContext.Run (
ec, capturedContext,
(fun _ -> (fun _ ->
let result = t () async {
tcs.SetResult () let! result =
m.Post MailboxMessage.Reconcile async {
rc.Reply result try
let! r = t ()
return Ok r
with e ->
return Error e
}
tcs.SetResult ()
m.Post MailboxMessage.Reconcile
rc.Reply result
}
|> Async.StartImmediate
), ),
() ()
) )
@@ -348,17 +362,36 @@ type ParallelQueue
let mb = new MailboxProcessor<_> (processTask MailboxState.Idle) let mb = new MailboxProcessor<_> (processTask MailboxState.Idle)
do mb.Start () do mb.Start ()
/// Request to run the given async action, freely in parallel with other running tests.
/// The resulting Task will return when the action has completed.
member _.RunAsync<'a>
(TestFixtureSetupToken parent)
(scope : Parallelizable<unit> option)
(action : unit -> Async<'a>)
: 'a Task
=
let ec = ExecutionContext.Capture ()
task {
let! result =
(fun rc -> MailboxMessage.RunTestAsync (parent, scope, AsyncThunkCrate.make action rc, ec))
|> mb.PostAndAsyncReply
|> Async.StartAsTask
match result with
| Ok o -> return o
| Error e -> return Exception.reraiseWithOriginalStackTrace e
}
/// Request to run the given action, freely in parallel with other running tests. /// Request to run the given action, freely in parallel with other running tests.
/// The resulting Task will return when the action has completed. /// The resulting Task will return when the action has completed.
member _.Run<'a> member this.Run<'a>
(TestFixtureSetupToken parent) (parent : TestFixtureSetupToken)
(scope : Parallelizable<unit> option) (scope : Parallelizable<unit> option)
(action : unit -> 'a) (action : unit -> 'a)
: 'a Task : 'a Task
= =
(fun rc -> MailboxMessage.RunTest (parent, scope, ThunkCrate.make action rc)) this.RunAsync parent scope (fun () -> async.Return (action ()))
|> mb.PostAndAsyncReply
|> Async.StartAsTask
/// Declare that we wish to start the given test fixture. The resulting Task will return /// Declare that we wish to start the given test fixture. The resulting Task will return
/// when you are allowed to start running tests from that fixture. /// when you are allowed to start running tests from that fixture.
@@ -379,11 +412,22 @@ type ParallelQueue
| Parallelizable.Yes _ -> Parallelizable.Yes () | Parallelizable.Yes _ -> Parallelizable.Yes ()
) )
let ec = ExecutionContext.Capture ()
let! response = let! response =
(fun rc -> MailboxMessage.RunTest (parent, par, ThunkCrate.make action rc)) (fun rc ->
MailboxMessage.RunTestAsync (
parent,
par,
AsyncThunkCrate.make (fun () -> async.Return (action ())) rc,
ec
)
)
|> mb.PostAndAsyncReply |> mb.PostAndAsyncReply
return response, TestFixtureSetupToken parent match response with
| Ok response -> return response, TestFixtureSetupToken parent
| Error e -> return Exception.reraiseWithOriginalStackTrace e
} }
/// Run the given one-time tear-down for the test fixture. /// Run the given one-time tear-down for the test fixture.
@@ -401,11 +445,22 @@ type ParallelQueue
| Parallelizable.Yes _ -> Parallelizable.Yes () | Parallelizable.Yes _ -> Parallelizable.Yes ()
) )
let ec = ExecutionContext.Capture ()
let! response = let! response =
(fun rc -> MailboxMessage.RunTest (parent, par, ThunkCrate.make action rc)) (fun rc ->
MailboxMessage.RunTestAsync (
parent,
par,
AsyncThunkCrate.make (fun () -> async.Return (action ())) rc,
ec
)
)
|> mb.PostAndAsyncReply |> mb.PostAndAsyncReply
return response, TestFixtureTearDownToken parent match response with
| Ok response -> return response, TestFixtureTearDownToken parent
| Error e -> return Exception.reraiseWithOriginalStackTrace e
} }
/// Declare that we have finished submitting requests to run in the given test fixture. /// Declare that we have finished submitting requests to run in the given test fixture.

View File

@@ -256,6 +256,7 @@ WoofWare.NUnitTestRunner.ParallelQueue inherit obj, implements IDisposable
WoofWare.NUnitTestRunner.ParallelQueue..ctor [constructor]: (int option, WoofWare.NUnitTestRunner.AssemblyParallelScope WoofWare.NUnitTestRunner.Parallelizable option, System.Threading.CancellationToken option) WoofWare.NUnitTestRunner.ParallelQueue..ctor [constructor]: (int option, WoofWare.NUnitTestRunner.AssemblyParallelScope WoofWare.NUnitTestRunner.Parallelizable option, System.Threading.CancellationToken option)
WoofWare.NUnitTestRunner.ParallelQueue.EndTestFixture [method]: WoofWare.NUnitTestRunner.TestFixtureTearDownToken -> unit System.Threading.Tasks.Task WoofWare.NUnitTestRunner.ParallelQueue.EndTestFixture [method]: WoofWare.NUnitTestRunner.TestFixtureTearDownToken -> unit System.Threading.Tasks.Task
WoofWare.NUnitTestRunner.ParallelQueue.Run [method]: WoofWare.NUnitTestRunner.TestFixtureSetupToken -> unit WoofWare.NUnitTestRunner.Parallelizable option -> (unit -> 'a) -> 'a System.Threading.Tasks.Task WoofWare.NUnitTestRunner.ParallelQueue.Run [method]: WoofWare.NUnitTestRunner.TestFixtureSetupToken -> unit WoofWare.NUnitTestRunner.Parallelizable option -> (unit -> 'a) -> 'a System.Threading.Tasks.Task
WoofWare.NUnitTestRunner.ParallelQueue.RunAsync [method]: WoofWare.NUnitTestRunner.TestFixtureSetupToken -> unit WoofWare.NUnitTestRunner.Parallelizable option -> (unit -> 'a Microsoft.FSharp.Control.FSharpAsync) -> 'a System.Threading.Tasks.Task
WoofWare.NUnitTestRunner.ParallelQueue.RunTestSetup [method]: WoofWare.NUnitTestRunner.TestFixtureRunningToken -> (unit -> 'a) -> ('a * WoofWare.NUnitTestRunner.TestFixtureSetupToken) System.Threading.Tasks.Task WoofWare.NUnitTestRunner.ParallelQueue.RunTestSetup [method]: WoofWare.NUnitTestRunner.TestFixtureRunningToken -> (unit -> 'a) -> ('a * WoofWare.NUnitTestRunner.TestFixtureSetupToken) System.Threading.Tasks.Task
WoofWare.NUnitTestRunner.ParallelQueue.RunTestTearDown [method]: WoofWare.NUnitTestRunner.TestFixtureSetupToken -> (unit -> 'a) -> ('a * WoofWare.NUnitTestRunner.TestFixtureTearDownToken) System.Threading.Tasks.Task WoofWare.NUnitTestRunner.ParallelQueue.RunTestTearDown [method]: WoofWare.NUnitTestRunner.TestFixtureSetupToken -> (unit -> 'a) -> ('a * WoofWare.NUnitTestRunner.TestFixtureTearDownToken) System.Threading.Tasks.Task
WoofWare.NUnitTestRunner.ParallelQueue.StartTestFixture [method]: WoofWare.NUnitTestRunner.TestFixture -> WoofWare.NUnitTestRunner.TestFixtureRunningToken System.Threading.Tasks.Task WoofWare.NUnitTestRunner.ParallelQueue.StartTestFixture [method]: WoofWare.NUnitTestRunner.TestFixture -> WoofWare.NUnitTestRunner.TestFixtureRunningToken System.Threading.Tasks.Task

View File

@@ -76,105 +76,168 @@ module TestFixture =
(test : MethodInfo) (test : MethodInfo)
(containingObject : obj) (containingObject : obj)
(args : obj[]) (args : obj[])
: Result<TestMemberSuccess, TestFailure list> * IndividualTestRunMetadata : Async<Result<TestMemberSuccess, TestFailure list> * IndividualTestRunMetadata>
= =
let rec runMethods let rec runMethods
(wrap : UserMethodFailure -> TestFailure) (wrap : UserMethodFailure -> TestFailure)
(toRun : MethodInfo list) (toRun : MethodInfo list)
(args : obj[]) (args : obj[])
: Result<unit, _> : Result<unit, TestFailure> Async
= =
match toRun with match toRun with
| [] -> Ok () | [] -> async.Return (Ok ())
| head :: rest -> | head :: rest ->
let result = async {
try let result =
head.Invoke (containingObject, args) |> Ok try
with head.Invoke (containingObject, args) |> Ok
| :? TargetInvocationException as e -> Error (UserMethodFailure.Threw (head.Name, e.InnerException)) with
| :? TargetParameterCountException -> | :? TargetInvocationException as e ->
UserMethodFailure.BadParameters ( Error (UserMethodFailure.Threw (head.Name, e.InnerException))
head.Name, | :? TargetParameterCountException ->
head.GetParameters () |> Array.map (fun pm -> pm.ParameterType), UserMethodFailure.BadParameters (
args head.Name,
) head.GetParameters () |> Array.map (fun pm -> pm.ParameterType),
|> Error args
)
|> Error
match result with let! ct = Async.CancellationToken
| Error e -> Error (wrap e)
| Ok result ->
match result with
| :? unit -> runMethods wrap rest args
| ret -> UserMethodFailure.ReturnedNonUnit (head.Name, ret) |> wrap |> Error
let start = DateTimeOffset.Now let! result =
match result with
| Error e -> async.Return (Error (wrap e))
| Ok result ->
match result with
| :? unit -> runMethods wrap rest args
| :? Task as result ->
async {
let mutable exc = None
let sw = Stopwatch.StartNew () try
do! Async.AwaitTask result
with e ->
exc <- Some e
let metadata () = match exc with
let name = | None -> return! runMethods wrap rest args
if args.Length = 0 then | Some e -> return Error (UserMethodFailure.Threw (head.Name, e) |> wrap)
test.Name }
else // We'd like to do this type-test:
let argsStr = args |> Seq.map string<obj> |> String.concat "," // | :? Async<unit> as result ->
$"%s{test.Name}(%s{argsStr})" // but instead we have to do all this reflective nonsense, because FSharpAsync is not part
// of the .NET runtime, so is instead in a different AssemblyLoadContext to us!
// It's in the user-code context, not ours.
| ret ->
let ty = ret.GetType ()
{ if ty.Namespace = "Microsoft.FSharp.Control" && ty.Name = "FSharpAsync`1" then
End = DateTimeOffset.Now match ty.GenericTypeArguments |> Array.map (fun t -> t.FullName) with
Start = start | [| "Microsoft.FSharp.Core.Unit" |] ->
Total = sw.Elapsed let asyncModule = ty.Assembly.GetType ("Microsoft.FSharp.Control.FSharpAsync")
ComputerName = Environment.MachineName // let catch = asyncModule.GetMethod("Catch").MakeGenericMethod [| ty.GenericTypeArguments.[0] |]
ExecutionId = Guid.NewGuid () // let caught = catch.Invoke ((null: obj), [| ret |])
TestId = testId let startAsTask =
TestName = name asyncModule.GetMethod("StartAsTask").MakeGenericMethod
ClassName = test.DeclaringType.FullName [| ty.GenericTypeArguments.[0] |]
StdOut =
match contexts.DumpStdout outputId with
| "" -> None
| v -> Some v
StdErr =
match contexts.DumpStderr outputId with
| "" -> None
| v -> Some v
}
let setUpResult = runMethods TestFailure.SetUpFailed setUp [||] let started =
sw.Stop () startAsTask.Invoke ((null : obj), [| ret ; (null : obj) ; (null : obj) |])
|> unbox<Task>
match setUpResult with async {
| Error e -> Error [ e ], metadata () let! res = Async.AwaitTask started |> Async.Catch
| Ok () ->
sw.Start () match res with
| Choice1Of2 () -> return! runMethods wrap rest args
| Choice2Of2 e ->
return
Error (
UserMethodFailure.Threw (head.Name, started.Exception) |> wrap
)
}
| _ ->
UserMethodFailure.ReturnedNonUnit (head.Name, ret)
|> wrap
|> Error
|> async.Return
else
async.Return (UserMethodFailure.ReturnedNonUnit (head.Name, ret) |> wrap |> Error)
let result = return result
let result = runMethods TestFailure.TestFailed [ test ] args }
async {
let start = DateTimeOffset.Now
let sw = Stopwatch.StartNew ()
let metadata () =
let name =
if args.Length = 0 then
test.Name
else
let argsStr = args |> Seq.map string<obj> |> String.concat ","
$"%s{test.Name}(%s{argsStr})"
{
End = DateTimeOffset.Now
Start = start
Total = sw.Elapsed
ComputerName = Environment.MachineName
ExecutionId = Guid.NewGuid ()
TestId = testId
TestName = name
ClassName = test.DeclaringType.FullName
StdOut =
match contexts.DumpStdout outputId with
| "" -> None
| v -> Some v
StdErr =
match contexts.DumpStderr outputId with
| "" -> None
| v -> Some v
}
let! setUpResult = runMethods TestFailure.SetUpFailed setUp [||]
sw.Stop () sw.Stop ()
match result with match setUpResult with
| Ok () -> Ok None | Error e -> return Error [ e ], metadata ()
| Error (TestFailure.TestFailed (UserMethodFailure.Threw (_, exc)) as orig) -> | Ok () ->
match exc.GetType().FullName with
| "NUnit.Framework.SuccessException" -> Ok None
| "NUnit.Framework.IgnoreException" -> Ok (Some (TestMemberSuccess.Ignored (Option.ofObj exc.Message)))
| "NUnit.Framework.InconclusiveException" ->
Ok (Some (TestMemberSuccess.Inconclusive (Option.ofObj exc.Message)))
| _ -> Error orig
| Error orig -> Error orig
// Unconditionally run TearDown after tests, even if tests failed. sw.Start ()
sw.Start () let! result = runMethods TestFailure.TestFailed [ test ] args
let tearDownResult = runMethods TestFailure.TearDownFailed tearDown [||] sw.Stop ()
sw.Stop ()
let metadata = metadata () let result =
match result with
| Ok () -> Ok None
| Error (TestFailure.TestFailed (UserMethodFailure.Threw (_, exc)) as orig) ->
match exc.GetType().FullName with
| "NUnit.Framework.SuccessException" -> Ok None
| "NUnit.Framework.IgnoreException" ->
Ok (Some (TestMemberSuccess.Ignored (Option.ofObj exc.Message)))
| "NUnit.Framework.InconclusiveException" ->
Ok (Some (TestMemberSuccess.Inconclusive (Option.ofObj exc.Message)))
| _ -> Error orig
| Error orig -> Error orig
match result, tearDownResult with // Unconditionally run TearDown after tests, even if tests failed.
| Ok None, Ok () -> Ok TestMemberSuccess.Ok, metadata sw.Start ()
| Ok (Some s), Ok () -> Ok s, metadata let! tearDownResult = runMethods TestFailure.TearDownFailed tearDown [||]
| Error e, Ok () sw.Stop ()
| Ok _, Error e -> Error [ e ], metadata
| Error e1, Error e2 -> Error [ e1 ; e2 ], metadata let metadata = metadata ()
return
match result, tearDownResult with
| Ok None, Ok () -> Ok TestMemberSuccess.Ok, metadata
| Ok (Some s), Ok () -> Ok s, metadata
| Error e, Ok ()
| Ok _, Error e -> Error [ e ], metadata
| Error e1, Error e2 -> Error [ e1 ; e2 ], metadata
}
let private getValues (test : SingleTestMethod) = let private getValues (test : SingleTestMethod) =
let valuesAttrs = let valuesAttrs =
@@ -395,20 +458,22 @@ module TestFixture =
|> Seq.map (fun (testGuid, args) -> |> Seq.map (fun (testGuid, args) ->
task { task {
let runMe () = let runMe () =
progress.OnTestMemberStart test.Name async {
let oldValue = contexts.AsyncLocal.Value progress.OnTestMemberStart test.Name
let outputId = contexts.NewOutputs () let oldValue = contexts.AsyncLocal.Value
contexts.AsyncLocal.Value <- outputId let outputId = contexts.NewOutputs ()
contexts.AsyncLocal.Value <- outputId
let result, meta = let! result, meta =
runOne outputId contexts setUp tearDown testGuid test.Method containingObject args runOne outputId contexts setUp tearDown testGuid test.Method containingObject args
contexts.AsyncLocal.Value <- oldValue contexts.AsyncLocal.Value <- oldValue
progress.OnTestMemberFinished test.Name progress.OnTestMemberFinished test.Name
result, meta return result, meta
}
let! results, summary = par.Run running test.Parallelize runMe let! results, summary = par.RunAsync running test.Parallelize runMe
match results with match results with
| Ok results -> return Ok results, summary | Ok results -> return Ok results, summary

View File

@@ -14,7 +14,7 @@
<PackageId>WoofWare.NUnitTestRunner.Lib</PackageId> <PackageId>WoofWare.NUnitTestRunner.Lib</PackageId>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarnOn>FS3559</WarnOn> <WarnOn>FS3559</WarnOn>
<WoofWareMyriadPluginVersion>8.0.4</WoofWareMyriadPluginVersion> <WoofWareMyriadPluginVersion>8.1.1</WoofWareMyriadPluginVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -31,6 +31,7 @@
<Compile Include="ParallelScope.fs" /> <Compile Include="ParallelScope.fs" />
<Compile Include="DotnetRuntime.fs" /> <Compile Include="DotnetRuntime.fs" />
<Compile Include="Array.fs" /> <Compile Include="Array.fs" />
<Compile Include="Exception.fs" />
<Compile Include="List.fs" /> <Compile Include="List.fs" />
<Compile Include="Result.fs" /> <Compile Include="Result.fs" />
<Compile Include="Domain.fs" /> <Compile Include="Domain.fs" />

View File

@@ -1,5 +1,5 @@
{ {
"version": "0.21", "version": "0.22",
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^refs/heads/main$" "^refs/heads/main$"
], ],
@@ -8,4 +8,4 @@
":/Directory.Build.props", ":/Directory.Build.props",
":/README.md" ":/README.md"
] ]
} }

View File

@@ -0,0 +1,396 @@
namespace WoofWare.NUnitTestRunner.Test
open System
open System.Text
open System.Threading
open System.Threading.Tasks
open NUnit.Framework
open FsUnitTyped
open WoofWare.NUnitTestRunner
[<TestFixture>]
module TestSynchronizationContext =
[<Test>]
let ``ExecutionContext flows correctly through synchronous operations`` () =
task {
let dummyFixture =
TestFixture.Empty typeof<obj> (Some (Parallelizable.Yes ClassParallelScope.All)) [] []
use contexts = TestContexts.Empty ()
use queue = new ParallelQueue (Some 4, None)
// Track which context values we see during execution
let contextValues = System.Collections.Concurrent.ConcurrentBag<Guid * Guid> ()
// Start the fixture
let! running = queue.StartTestFixture dummyFixture
let! _, setup = queue.RunTestSetup running (fun () -> ())
// Create several synchronous operations with different context values
let tasks =
[ 1..10 ]
|> List.map (fun _ ->
task {
do! Task.Yield ()
// Set a unique context value
let outputId = contexts.NewOutputs ()
let (OutputStreamId expectedId) = outputId
contexts.AsyncLocal.Value <- outputId
// Run a synchronous operation that checks the context
let! actualId =
queue.Run
setup
None
(fun () ->
// Check context immediately
let immediate = contexts.AsyncLocal.Value
let (OutputStreamId immediateGuid) = immediate
contextValues.Add (expectedId, immediateGuid)
// Do some work that might cause context issues
Thread.Sleep 10
// Check context after work
let afterWork = contexts.AsyncLocal.Value
let (OutputStreamId afterWorkGuid) = afterWork
contextValues.Add (expectedId, afterWorkGuid)
// Simulate calling into framework code that might use ExecutionContext
let mutable capturedValue = Guid.Empty
ExecutionContext.Run (
ExecutionContext.Capture (),
(fun _ ->
let current = contexts.AsyncLocal.Value
let (OutputStreamId currentGuid) = current
capturedValue <- currentGuid
),
()
)
contextValues.Add (expectedId, capturedValue)
afterWorkGuid
)
// Verify the returned value matches what we set
actualId |> shouldEqual expectedId
}
)
// Wait for all tasks
let! results = Task.WhenAll tasks
results |> Array.iter id
// Verify all context values were correct
let allValues = contextValues |> Seq.toList
allValues |> shouldHaveLength 30 // 3 checks per operation * 10 operations
// Every captured value should match its expected value
allValues
|> List.iter (fun (expected, actual) -> actual |> shouldEqual expected)
// Clean up
let! _, teardown = queue.RunTestTearDown setup (fun () -> ())
do! queue.EndTestFixture teardown
}
[<Test>]
let ``ExecutionContext isolation between concurrent synchronous operations`` () =
task {
let dummyFixture =
TestFixture.Empty typeof<obj> (Some (Parallelizable.Yes ClassParallelScope.All)) [] []
use contexts = TestContexts.Empty ()
use queue = new ParallelQueue (Some 4, None)
let! running = queue.StartTestFixture dummyFixture
let! _, setup = queue.RunTestSetup running (fun () -> ())
// Use a barrier to ensure operations run concurrently
let barrier = new Barrier (3)
let seenValues = System.Collections.Concurrent.ConcurrentBag<int * Guid> ()
let outputIds = System.Collections.Concurrent.ConcurrentBag<OutputStreamId> ()
// Create operations that will definitely run concurrently
let tasks =
[ 1..3 ]
|> List.map (fun i ->
task {
// Each task sets its own context value
let outputId = contexts.NewOutputs ()
let (OutputStreamId myId) = outputId
contexts.AsyncLocal.Value <- outputId
outputIds.Add outputId
let! result =
queue.Run
setup
(Some (Parallelizable.Yes ()))
(fun () ->
// Wait for all tasks to reach this point
barrier.SignalAndWait ()
// Now check what value we see
let currentValue = contexts.AsyncLocal.Value
match currentValue with
| OutputStreamId guid -> seenValues.Add (i, guid)
// Do some synchronous work
Thread.Sleep 5
// Check again after work
let afterWork = contexts.AsyncLocal.Value
match afterWork with
| OutputStreamId guid ->
// Also verify we can write to the correct streams
contexts.Stdout.WriteLine $"Task %i{i} sees context %O{guid}"
guid
)
// Each task should see its own value
result |> shouldEqual myId
}
)
let! results = Task.WhenAll tasks
results |> Array.iter id
// Verify we saw 3 different values (one per task)
let values = seenValues |> Seq.toList
values |> shouldHaveLength 3
// All seen values should be different (no context bleeding)
let uniqueValues = values |> List.map snd |> List.distinct
uniqueValues |> shouldHaveLength 3
let! _, teardown = queue.RunTestTearDown setup (fun () -> ())
do! queue.EndTestFixture teardown
// Verify stdout content for each task
let collectedOutputs = outputIds |> Seq.toList
collectedOutputs |> shouldHaveLength 3
for outputId in collectedOutputs do
let content = contexts.DumpStdout outputId
content |> shouldNotEqual ""
let (OutputStreamId guid) = outputId
content |> shouldContainText (guid.ToString ())
}
[<Test>]
let ``ExecutionContext flows correctly through nested synchronous operations`` () =
task {
let dummyFixture =
TestFixture.Empty typeof<obj> (Some (Parallelizable.Yes ClassParallelScope.All)) [] []
use contexts = TestContexts.Empty ()
use queue = new ParallelQueue (Some 4, None)
let! running = queue.StartTestFixture dummyFixture
let! _, setup = queue.RunTestSetup running (fun () -> ())
// Set an initial context
let outputId = contexts.NewOutputs ()
let (OutputStreamId outerGuid) = outputId
contexts.AsyncLocal.Value <- outputId
let! result =
queue.Run
setup
None
(fun () ->
// Check we have the outer context
let outer = contexts.AsyncLocal.Value
let (OutputStreamId outerSeen) = outer
outerSeen |> shouldEqual outerGuid
// Now change the context for a nested operation
let innerOutputId = contexts.NewOutputs ()
let (OutputStreamId innerGuid) = innerOutputId
contexts.AsyncLocal.Value <- innerOutputId
// Use Task.Run to potentially hop threads
let innerResult =
Task
.Run(fun () ->
let inner = contexts.AsyncLocal.Value
let (OutputStreamId innerSeen) = inner
innerSeen |> shouldEqual innerGuid
innerSeen
)
.Result
// After the nested operation, we should still have our inner context
let afterNested = contexts.AsyncLocal.Value
let (OutputStreamId afterNestedGuid) = afterNested
afterNestedGuid |> shouldEqual innerGuid
(outerSeen, innerResult, afterNestedGuid)
)
// Unpack results
let seenOuter, seenInner, seenAfter = result
seenOuter |> shouldEqual outerGuid
seenInner |> shouldNotEqual outerGuid
seenAfter |> shouldEqual seenInner
let! _, teardown = queue.RunTestTearDown setup (fun () -> ())
do! queue.EndTestFixture teardown
}
[<Test>]
let ``ExecutionContext flows correctly through async operations`` () =
task {
// Create a test fixture
let dummyFixture =
TestFixture.Empty typeof<obj> (Some (Parallelizable.Yes ClassParallelScope.All)) [] []
use contexts = TestContexts.Empty ()
use queue = new ParallelQueue (Some 4, None)
// Track which context values we see during execution
let contextValues = System.Collections.Concurrent.ConcurrentBag<Guid * Guid> ()
// Start the fixture
let! running = queue.StartTestFixture dummyFixture
let! _, setup = queue.RunTestSetup running (fun () -> ())
// Create several async operations with different context values
let tasks =
[ 1..10 ]
|> List.map (fun i ->
task {
// Set a unique context value
let expectedId = Guid.NewGuid ()
let outputId = OutputStreamId expectedId
contexts.AsyncLocal.Value <- outputId
// Run an async operation that checks the context at multiple points
let! actualId =
queue.RunAsync
setup
None
(fun () ->
async {
// Check context immediately
let immediate = contexts.AsyncLocal.Value
let (OutputStreamId immediateGuid) = immediate
contextValues.Add (expectedId, immediateGuid)
// Yield to allow potential context loss
do! Async.Sleep 10
// Check context after yield
let afterYield = contexts.AsyncLocal.Value
let (OutputStreamId afterYieldGuid) = afterYield
contextValues.Add (expectedId, afterYieldGuid)
// Do some actual async work
do! Task.Delay (10) |> Async.AwaitTask
// Check context after task
let afterTask = contexts.AsyncLocal.Value
let (OutputStreamId afterTaskGuid) = afterTask
contextValues.Add (expectedId, afterTaskGuid)
return afterTaskGuid
}
)
// Verify the returned value matches what we set
actualId |> shouldEqual expectedId
}
)
// Wait for all tasks
let! results = Task.WhenAll (tasks)
results |> Array.iter id
// Verify all context values were correct
let allValues = contextValues |> Seq.toList
allValues |> shouldHaveLength 30 // 3 checks per operation * 10 operations
// Every captured value should match its expected value
allValues
|> List.iter (fun (expected, actual) -> actual |> shouldEqual expected)
// Clean up
let! _, teardown = queue.RunTestTearDown setup (fun () -> ())
do! queue.EndTestFixture teardown
}
[<Test>]
let ``ExecutionContext isolation between concurrent operations`` () =
task {
let dummyFixture =
TestFixture.Empty typeof<obj> (Some (Parallelizable.Yes ClassParallelScope.All)) [] []
use contexts = TestContexts.Empty ()
use queue = new ParallelQueue (Some 4, None)
let! running = queue.StartTestFixture dummyFixture
let! _, setup = queue.RunTestSetup running (fun () -> ())
// Use a barrier to ensure operations run concurrently
let barrier = new Barrier (3)
let seenValues = System.Collections.Concurrent.ConcurrentBag<int * Guid> ()
// Create operations that will definitely run concurrently
let tasks =
[ 1..3 ]
|> List.map (fun i ->
task {
// Each task sets its own context value
let myId = Guid.NewGuid ()
contexts.AsyncLocal.Value <- OutputStreamId myId
let! result =
queue.RunAsync
setup
(Some (Parallelizable.Yes ()))
(fun () ->
async {
// Wait for all tasks to reach this point
barrier.SignalAndWait () |> ignore
// Now check what value we see
let currentValue = contexts.AsyncLocal.Value
match currentValue with
| OutputStreamId guid -> seenValues.Add (i, guid)
// Do some async work
do! Async.Sleep 5
// Check again after async work
let afterAsync = contexts.AsyncLocal.Value
match afterAsync with
| OutputStreamId guid -> return guid
}
)
// Each task should see its own value
result |> shouldEqual myId
}
)
let! results = Task.WhenAll (tasks)
results |> Array.iter id
// Verify we saw 3 different values (one per task)
let values = seenValues |> Seq.toList
values |> shouldHaveLength 3
// All seen values should be different (no context bleeding)
let uniqueValues = values |> List.map snd |> List.distinct
uniqueValues |> shouldHaveLength 3
let! _, teardown = queue.RunTestTearDown setup (fun () -> ())
do! queue.EndTestFixture teardown
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
</PropertyGroup> </PropertyGroup>
@@ -11,14 +11,15 @@
<Compile Include="TestFilter.fs" /> <Compile Include="TestFilter.fs" />
<Compile Include="TestList.fs" /> <Compile Include="TestList.fs" />
<Compile Include="TestSurface.fs" /> <Compile Include="TestSurface.fs" />
<Compile Include="TestSynchronizationContext.fs" />
<Compile Include="TestTrx.fs" /> <Compile Include="TestTrx.fs" />
<EmbeddedResource Include="Example1.trx" /> <EmbeddedResource Include="Example1.trx" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.1.21" /> <PackageReference Include="ApiSurface" Version="5.0.2" />
<PackageReference Include="FsCheck" Version="3.3.0" /> <PackageReference Include="FsCheck" Version="3.3.1" />
<PackageReference Include="FsUnit" Version="7.0.1" /> <PackageReference Include="FsUnit" Version="7.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="NUnit" Version="4.3.2" /> <PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/> <PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<RollForward>Major</RollForward> <RollForward>Major</RollForward>
<PackAsTool>true</PackAsTool> <PackAsTool>true</PackAsTool>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -35,7 +35,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Spectre.Console" Version="0.50.0" /> <PackageReference Include="Spectre.Console" Version="0.51.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,5 +1,5 @@
{ {
"version": "0.2", "version": "0.3",
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^refs/heads/main$" "^refs/heads/main$"
], ],

View File

@@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.15.0]" /> <PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.17.0]" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1750386251, "lastModified": 1758262103,
"narHash": "sha256-1ovgdmuDYVo5OUC5NzdF+V4zx2uT8RtsgZahxidBTyw=", "narHash": "sha256-aBGl3XEOsjWw6W3AHiKibN7FeoG73dutQQEqnd/etR8=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "076e8c6678d8c54204abcb4b1b14c366835a58bb", "rev": "12bd230118a1901a4a5d393f9f56b6ad7e571d01",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -14,8 +14,8 @@
flake-utils.lib.eachDefaultSystem (system: let flake-utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
pname = "unofficial-nunit-runner"; pname = "unofficial-nunit-runner";
dotnet-sdk = pkgs.dotnetCorePackages.sdk_8_0; dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0;
dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0; dotnet-runtime = pkgs.dotnetCorePackages.runtime_9_0;
version = "0.1"; version = "0.1";
dotnetTool = dllOverride: toolName: toolVersion: hash: dotnetTool = dllOverride: toolName: toolVersion: hash:
pkgs.stdenvNoCC.mkDerivation rec { pkgs.stdenvNoCC.mkDerivation rec {

View File

@@ -1,13 +1,13 @@
[ [
{ {
"pname": "ApiSurface", "pname": "ApiSurface",
"version": "4.1.21", "version": "5.0.2",
"hash": "sha256-v2adBYoE9NZPaQR3u2qq9r/9PxAM/wqi2Uiky0xGq+E=" "hash": "sha256-zcq1H1ccQzsZQf4kolzoOBSbyz07skihgPAvQ9Jri+E="
}, },
{ {
"pname": "fantomas", "pname": "fantomas",
"version": "7.0.2", "version": "7.0.3",
"hash": "sha256-BAaENIm/ksTiXrUImRgKoIXTGIlgsX7ch6ayoFjhJXA=" "hash": "sha256-0XlfV7SxXPDnk/CjkUesJSaH0cxlNHJ+Jj86zNUhkNA="
}, },
{ {
"pname": "Fantomas.Core", "pname": "Fantomas.Core",
@@ -21,13 +21,18 @@
}, },
{ {
"pname": "FsCheck", "pname": "FsCheck",
"version": "3.3.0", "version": "3.3.1",
"hash": "sha256-TFDR/uAGv4OqrMX8/reQ4faaAhH9hxTHr1T/YkNPCCU=" "hash": "sha256-k65ksdOSOGz+meRUUND+yuqJtm5ChaKuaxmRIdKzx2Y="
}, },
{ {
"pname": "fsharp-analyzers", "pname": "fsharp-analyzers",
"version": "0.31.0", "version": "0.32.1",
"hash": "sha256-PoAvaXbXsmvVw870UsnqdD20HoBHO7u4bzoaz5DXfzM=" "hash": "sha256-le6rPnAF7cKGBZ2w8H2u9glK+6rT2ZjiAVnrkH2IhrM="
},
{
"pname": "FSharp.Core",
"version": "4.3.4",
"hash": "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk="
}, },
{ {
"pname": "FSharp.Core", "pname": "FSharp.Core",
@@ -36,13 +41,13 @@
}, },
{ {
"pname": "FSharp.Core", "pname": "FSharp.Core",
"version": "8.0.403", "version": "9.0.303",
"hash": "sha256-3XSQp7JUOU5T6gvSQXNfBF4t4gaX4J4xushH+cfM9mE=" "hash": "sha256-AxR6wqodeU23KOTgkUfIgbavgbcSuzD4UBP+tiFydgA="
}, },
{ {
"pname": "FsUnit", "pname": "FsUnit",
"version": "7.0.1", "version": "7.1.1",
"hash": "sha256-K85CIdxMeFSHEKZk6heIXp/oFjWAn7dBILKrw49pJUY=" "hash": "sha256-UMCEGKxQ4ytjmPuVpiNaAPbi3RQH9gqa61JJIUS/6hg="
}, },
{ {
"pname": "Microsoft.ApplicationInsights", "pname": "Microsoft.ApplicationInsights",
@@ -129,15 +134,20 @@
"version": "6.0.36", "version": "6.0.36",
"hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs=" "hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs="
}, },
{
"pname": "Microsoft.NETCore.Platforms",
"version": "1.1.0",
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
},
{ {
"pname": "Microsoft.NETCore.Platforms", "pname": "Microsoft.NETCore.Platforms",
"version": "1.1.1", "version": "1.1.1",
"hash": "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg=" "hash": "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg="
}, },
{ {
"pname": "Microsoft.NETCore.Platforms", "pname": "Microsoft.NETCore.Targets",
"version": "2.0.0", "version": "1.1.0",
"hash": "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro=" "hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
}, },
{ {
"pname": "Microsoft.NETCore.Targets", "pname": "Microsoft.NETCore.Targets",
@@ -196,8 +206,8 @@
}, },
{ {
"pname": "Nerdbank.GitVersioning", "pname": "Nerdbank.GitVersioning",
"version": "3.8.38-alpha", "version": "3.8.118",
"hash": "sha256-gPMrVbjOZxXoofczF/pn6eVkLhjVSJIyQrLO2oljrDc=" "hash": "sha256-Hmyy0ZKOmwN4zIhI4+MqoN8geZNc1sd033aZJ6APrO8="
}, },
{ {
"pname": "Newtonsoft.Json", "pname": "Newtonsoft.Json",
@@ -261,8 +271,8 @@
}, },
{ {
"pname": "Spectre.Console", "pname": "Spectre.Console",
"version": "0.50.0", "version": "0.51.1",
"hash": "sha256-3MNgumQSXzuXVGj7kLb5FMkTH/LoFohMvUjAZ7nyHfo=" "hash": "sha256-FQAK07dEwEsNYVI1T3S488LHv8AXJ+ZeA9N2hPpWBoo="
}, },
{ {
"pname": "System.Collections.Immutable", "pname": "System.Collections.Immutable",
@@ -284,36 +294,16 @@
"version": "6.0.0", "version": "6.0.0",
"hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8=" "hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8="
}, },
{
"pname": "System.IO.Abstractions",
"version": "4.2.13",
"hash": "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg="
},
{
"pname": "System.IO.FileSystem.AccessControl",
"version": "4.5.0",
"hash": "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8="
},
{ {
"pname": "System.Memory", "pname": "System.Memory",
"version": "4.5.5", "version": "4.5.5",
"hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI=" "hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI="
}, },
{
"pname": "System.Memory",
"version": "4.6.3",
"hash": "sha256-JgeK63WMmumF6L+FH5cwJgYdpqXrSDcgTQwtIgTHKVU="
},
{ {
"pname": "System.Private.Uri", "pname": "System.Private.Uri",
"version": "4.3.0", "version": "4.3.0",
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM=" "hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
}, },
{
"pname": "System.Reflection.Metadata",
"version": "1.6.0",
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
},
{ {
"pname": "System.Reflection.Metadata", "pname": "System.Reflection.Metadata",
"version": "8.0.0", "version": "8.0.0",
@@ -329,11 +319,6 @@
"version": "6.0.0", "version": "6.0.0",
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I=" "hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
}, },
{
"pname": "System.Security.AccessControl",
"version": "4.5.0",
"hash": "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM="
},
{ {
"pname": "System.Security.Cryptography.Pkcs", "pname": "System.Security.Cryptography.Pkcs",
"version": "6.0.4", "version": "6.0.4",
@@ -344,11 +329,6 @@
"version": "4.4.0", "version": "4.4.0",
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE=" "hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
}, },
{
"pname": "System.Security.Principal.Windows",
"version": "4.5.0",
"hash": "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY="
},
{ {
"pname": "System.Text.Json", "pname": "System.Text.Json",
"version": "8.0.5", "version": "8.0.5",
@@ -366,13 +346,13 @@
}, },
{ {
"pname": "WoofWare.Myriad.Plugins", "pname": "WoofWare.Myriad.Plugins",
"version": "8.0.4", "version": "8.1.1",
"hash": "sha256-/cX7B1QDOnyYRaetl1pQRkeVNLA7vP25TJfBZhi2j/c=" "hash": "sha256-Cx7/+5+qc8lhxb0On/OW58PrabadMfbcNP1TvLZGHrY="
}, },
{ {
"pname": "WoofWare.Myriad.Plugins.Attributes", "pname": "WoofWare.Myriad.Plugins.Attributes",
"version": "3.6.11", "version": "3.7.1",
"hash": "sha256-UI8wT1a3CuQUeXb1qIHCiimGJ1qBjK9ZOQN2/N5fNdg=" "hash": "sha256-Mn0OVvC/AwTYg6+3MCKLkNKGGSdzLfBOaLapxHsUDCw="
}, },
{ {
"pname": "WoofWare.PrattParser", "pname": "WoofWare.PrattParser",