Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 56 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ Basic:
```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.6.0
uses: Particular/run-tests-action@v1.8.0
```

With a reset script between each test run:

```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.6.0
uses: Particular/run-tests-action@v1.8.0
with:
reset-script: |
echo "Do whatever is necessary to reset the test infrastructure between runs of each framework"
Expand All @@ -33,7 +33,7 @@ In cases where the test matrix subdivides by target framework, you can also shor
```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.6.0
uses: Particular/run-tests-action@v1.8.0
with:
framework: net6.0
```
Expand All @@ -43,7 +43,7 @@ By default, only failed tests are reported. To report warnings for tests that ha
```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.6.0
uses: Particular/run-tests-action@v1.8.0
with:
report-warnings: true
```
Expand All @@ -53,11 +53,62 @@ By default, `dotnet test` uses `x64` as the target platform. This can be overrid
```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.6.0
uses: Particular/run-tests-action@v1.8.0
with:
target-platform: x86
```

## Running a subset of projects

By default the action discovers every `*.csproj` under `src/` that references `Microsoft.NET.Test.Sdk` and runs all of them. Pass `projects` to run an explicit, newline-delimited list of project paths instead, skipping discovery entirely. (Added in v1.8.0)

This is the intended integration point for repositories that subdivide their test suite by category and select a subset of assemblies per matrix job. For example, ServiceControl's [`tools/select-test-projects.ps1`](https://github.com/Particular/ServiceControl/blob/master/tools/select-test-projects.ps1) writes each category's project list to `$GITHUB_OUTPUT` as a multiline `test-projects` value, which can be passed straight through:

```yaml
steps:
- id: select
shell: pwsh
run: ./tools/select-test-projects.ps1
- name: Run tests
uses: Particular/run-tests-action@v1.8.0
with:
projects: ${{ steps.select.outputs.test-projects }}
```

When `projects` is combined with `framework`, each listed project is run only against that framework (projects that do not target it are skipped), mirroring the behavior of the discovery path.

## Parallel execution

By default the action runs `dotnet test` sequentially. Pass `max-parallel` (1–16) to run several test assemblies concurrently. (Added in v1.8.0)

```yaml
steps:
- name: Run tests
uses: Particular/run-tests-action@v1.8.0
with:
projects: ${{ steps.select.outputs.test-projects }}
max-parallel: 4
```

When `max-parallel > 1`, each run's stdout and stderr are buffered to temp files and replayed inside a `::group::` block once that run completes, because interleaved live `dotnet test` output is unreadable. The step fails if any run exits non-zero.

### Per-run parallel index

Every spawned `dotnet test` process has the environment variable `PARTICULAR_RUN_TESTS_ACTION_PARALLEL_INDEX` set to its 0-based position in the flattened run list, immediately before it is spawned (so the child inherits it). The value is unique across all runs in the invocation, so concurrent runs always see distinct indices. In sequential mode (`max-parallel == 1`) the index is always `0`.

Consumers that need per-run distinct resources — ports, temp directories, or anything else — can read this env var and derive what they need from the index. The action itself does no port arithmetic, keeping it repository-agnostic. For example, a suite using RavenDB.Embedded (which binds a fixed port and would otherwise collide across concurrent runs) can compute its port from the index:

```csharp
var index = int.Parse(Environment.GetEnvironmentVariable("PARTICULAR_RUN_TESTS_ACTION_PARALLEL_INDEX") ?? "0");
var port = 33334 + (index * 10);
```

Consumers that do not need per-run distinction simply ignore the variable.

### Interaction with `reset-script`

`reset-script` runs between consecutive target frameworks on the sequential path (`max-parallel == 1`), as it always has. When `max-parallel > 1`, runs are flattened across frameworks, so "between frameworks" no longer has a meaningful boundary and running the script concurrently with in-flight test processes is unsafe. In that case the reset script is ignored and the action emits a `::warning::` to make the skip visible. If you need a reset between batches, run sequential (`max-parallel: 1`) or invoke the reset script from a separate workflow step.

## What about filters?

This action does not support the [dotnet test filter syntax](https://learn.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests). This is because it's impossible to distinguish between the following cases:
Expand Down
20 changes: 19 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: 'Run tests'
description: 'Runs dotnet test using target frameworks appropriate for the current platform'
inputs:
reset-script:
description: 'pwsh expression to be run between test runs to reset infrastructure, if required'
description: 'pwsh expression to be run between test runs to reset infrastructure, if required. Only used when max-parallel is 1; ignored (with a warning) when max-parallel > 1.'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is a fair tradeoff

required: false
framework:
description: Specifies the target framework to run tests for
Expand All @@ -14,6 +14,22 @@ inputs:
description: Specifies the RunConfiguration.TargetPlatform for dotnet test. Defaults to 'x64'.
required: false
default: x64
projects:
description: >-
Optional newline-delimited list of project paths to run instead of
auto-discovering test projects. When provided, project discovery is
skipped and only the listed projects are tested. When absent, today's
discovery behavior (all *.csproj under src/ referencing
Microsoft.NET.Test.Sdk) is preserved.
required: false
max-parallel:
description: >-
Maximum number of `dotnet test` processes to run concurrently. Defaults
to 1 (sequential), which preserves the historic behavior. Values greater
than 1 enable parallel execution with buffered, per-run output replay,
and disable reset-script.
required: false
default: '1'
runs:
using: "composite"
steps:
Expand All @@ -37,4 +53,6 @@ runs:
REPORT_WARNINGS: ${{ inputs.report-warnings }}
TARGET_PLATFORM: ${{ inputs.target-platform }}
TEST_FILTER: ${{ inputs.filter }}
TEST_PROJECTS: ${{ inputs.projects }}
MAX_PARALLEL: ${{ inputs.max-parallel }}
run: ${{ github.action_path }}/run-tests.ps1
Loading