Skip to content
Merged
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
80 changes: 63 additions & 17 deletions .github/skills/vstest-build-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,30 +90,75 @@ For isolated projects with few dependencies:
./test.cmd
```

### Specific Test Assembly
### Specific Project(s)

Use `-p` to filter by assembly name pattern:
`-projects` / `--projects` takes a **resolvable path or glob** — it is passed through
`Resolve-Path`, so a bare project nickname or category (e.g. `smoke`, `htmllogger`) fails with
`Cannot find path`. Point it at the csproj(s):

```bash
# Windows
./test.cmd -projects "test\**\*HtmlLogger*\*.csproj"

# Linux / macOS
./test.sh -p htmllogger # HTML logger tests
./test.sh -p trxlogger # TRX logger tests
./test.sh -p datacollector # Data collector tests
./test.sh -p smoke # Smoke tests

# Windows (-p is ambiguous in PowerShell; use -projects)
./test.cmd -projects htmllogger
./test.cmd -projects smoke
./test.sh --projects "test/**/*HtmlLogger*/*.csproj"
```

### Specific Test by Name
For a single project you can also build+test its csproj directly with the bootstrapped SDK:

```bash
# Windows
./test.cmd -bl -c release /p:TestRunnerAdditionalArguments="'--filter TestName'" -Integration
./.dotnet/dotnet.exe test test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/*.csproj -c Debug

# Linux / macOS
./test.sh -bl -c release /p:TestRunnerAdditionalArguments="'--filter TestName'" --integrationTest
./.dotnet/dotnet test test/Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests/*.csproj -c Debug
```

### Test Categories (smoke / integration / performance / compatibility)

These are **switches** handled by `eng/build.ps1` — NOT `-projects` values:

```bash
# Windows
./test.cmd -smokeTest # TestCategory=Smoke (a subset of integration tests)
./test.cmd -integrationTest # full acceptance / integration suite
./test.cmd -performanceTest
./test.cmd -compatibilityTest

# Linux / macOS use the same switch names
./test.sh -smokeTest
```

> `-smokeTest` and `-integrationTest` are mutually exclusive (smoke is a subset); passing both throws.

### Filter by Test Name

Use the `-filter` parameter. Do **not** pass `--filter` inside `TestRunnerAdditionalArguments` —
`eng/build.ps1` explicitly throws if you do.

```bash
# Windows
./test.cmd -integrationTest -filter "FullyQualifiedName~MyScenario"

# Linux / macOS
./test.sh -integrationTest -filter "FullyQualifiedName~MyScenario"
```

### Running integration / smoke tests locally (DOTNET_ROOT gotcha)

Integration and smoke tests self-host: they launch test-asset apphosts built against the repo's
preview TFM (e.g. `net11.0`). An apphost resolves its shared runtime from `DOTNET_ROOT`, falling
back to the machine-wide install (`C:\Program Files\dotnet`), which usually lacks the preview
runtime — so it fails instantly with *"You must install or update .NET to run this application."*

- `test.sh` (Linux/macOS) sets `DOTNET_ROOT` to the repo `.dotnet` automatically.
- `test.cmd` (Windows) does **not** — set it yourself before running:

```powershell
$env:DOTNET_ROOT = "$PWD\.dotnet"
${env:DOTNET_ROOT(x86)} = "$PWD\.dotnet\dotnet-sdk-x86" # only if x86 test hosts run
$env:DOTNET_MULTILEVEL_LOOKUP = "0"
./test.cmd -smokeTest
```

## Manual Validation with vstest.console
Expand All @@ -131,15 +176,16 @@ After building with `--pack` / `-pack`, validate vstest.console changes by unzip

## Test Categories

| Category | Speed | What it tests | Filter |
| Category | Speed | What it tests | How to run |
|---|---|---|---|
| Unit tests | Fast | Individual units | `./test.sh` / `./test.cmd` (default) |
| Smoke tests | Slow | P0 end-to-end scenarios | `-p smoke` |
| Acceptance tests | Slowest | Extensive coverage | `--integrationTest` / `-Integration` flag |
| Unit tests | Fast | Individual units | `./test.cmd` / `./test.sh` (default) |
| Smoke tests | Slow | P0 end-to-end scenarios | `-smokeTest` switch |
| Acceptance / integration | Slowest | Extensive coverage | `-integrationTest` switch |

## Troubleshooting

- **OS mismatch errors:** If you see SDK load failures, run the mismatch detection script above to clean and re-bootstrap.
- **Integration/smoke tests fail instantly on Windows with "You must install or update .NET to run this application":** `test.cmd` does not set `DOTNET_ROOT`, so the self-hosted preview-TFM apphosts look in `C:\Program Files\dotnet` (which lacks the preview runtime). Set `$env:DOTNET_ROOT = "$PWD\.dotnet"` before running — see "Running integration / smoke tests locally".
- If build fails asking for .NET 4.6 targeting pack, install it from [Microsoft Downloads](https://www.microsoft.com/download/details.aspx?id=48136)
- Enable verbose diagnostics: see `docs/diagnose.md`
- For debugging, add `Debugger.Launch` at process entry points (testhost.exe, vstest.console.exe)
12 changes: 10 additions & 2 deletions src/vstest.console/CommandLine/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors;
using Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers;
using Microsoft.VisualStudio.TestPlatform.Common;
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.Common.Utilities;
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing;
using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Tracing.Interfaces;
Expand Down Expand Up @@ -59,6 +60,7 @@ internal class Executor
private readonly ITestPlatformEventSource _testPlatformEventSource;
private readonly IProcessHelper _processHelper;
private readonly IEnvironment _environment;
private readonly IRunSettingsProvider _runSettingsProvider;
private bool _showHelp;

/// <summary>
Expand Down Expand Up @@ -89,6 +91,11 @@ internal class Executor
}

internal Executor(IOutput output, ITestPlatformEventSource testPlatformEventSource, IProcessHelper processHelper, IEnvironment environment)
: this(output, testPlatformEventSource, processHelper, environment, RunSettingsManager.Instance)
{
}

internal Executor(IOutput output, ITestPlatformEventSource testPlatformEventSource, IProcessHelper processHelper, IEnvironment environment, IRunSettingsProvider runSettingsProvider)
{
DebuggerBreakpoint.AttachVisualStudioDebugger(WellKnownDebugEnvironmentVariables.VSTEST_RUNNER_DEBUG_ATTACHVS);
DebuggerBreakpoint.WaitForNativeDebugger(WellKnownDebugEnvironmentVariables.VSTEST_RUNNER_NATIVE_DEBUG);
Expand All @@ -99,6 +106,7 @@ internal Executor(IOutput output, ITestPlatformEventSource testPlatformEventSour
_showHelp = true;
_processHelper = processHelper;
_environment = environment;
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
Expand Down Expand Up @@ -220,7 +228,7 @@ private int GetArgumentProcessors(string[] args, out List<IArgumentProcessor> pr
{
processors = new List<IArgumentProcessor>();
int result = 0;
var processorFactory = ArgumentProcessorFactory.Create();
var processorFactory = ArgumentProcessorFactory.Create(runSettingsProvider: _runSettingsProvider);
for (var index = 0; index < args.Length; index++)
{
var arg = args[index];
Expand Down Expand Up @@ -268,7 +276,7 @@ private int GetArgumentProcessors(string[] args, out List<IArgumentProcessor> pr
}

// Initialize Runsettings with defaults
RunSettingsManager.Instance.AddDefaultRunSettings();
_runSettingsProvider.AddDefaultRunSettings();

// Ensure we have an action argument.
EnsureActionArgumentIsPresent(processors, processorFactory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ internal class CliRunSettingsArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public CliRunSettingsArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -43,7 +49,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new CliRunSettingsArgumentExecutor(RunSettingsManager.Instance, CommandLineOptions.Instance));
new CliRunSettingsArgumentExecutor(_runSettingsProvider, CommandLineOptions.Instance));

set => _executor = value;
}
Expand Down
8 changes: 7 additions & 1 deletion src/vstest.console/Processors/CollectArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ internal class CollectArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public CollectArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -49,7 +55,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new CollectArgumentExecutor(RunSettingsManager.Instance, new FileHelper()));
new CollectArgumentExecutor(_runSettingsProvider, new FileHelper()));

set => _executor = value;
}
Expand Down
6 changes: 4 additions & 2 deletions src/vstest.console/Processors/EnableBlameArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ internal class EnableBlameArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

/// <summary>
/// Initializes a new instance of the <see cref="EnableBlameArgumentProcessor"/> class.
/// </summary>
public EnableBlameArgumentProcessor()
public EnableBlameArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

public Lazy<IArgumentProcessorCapabilities> Metadata
Expand All @@ -58,7 +60,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new EnableBlameArgumentExecutor(RunSettingsManager.Instance, new PlatformEnvironment(), new FileHelper()));
new EnableBlameArgumentExecutor(_runSettingsProvider, new PlatformEnvironment(), new FileHelper()));

set => _executor = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ internal class EnableCodeCoverageArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public EnableCodeCoverageArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -43,7 +49,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new EnableCodeCoverageArgumentExecutor(CommandLineOptions.Instance, RunSettingsManager.Instance, new FileHelper()));
new EnableCodeCoverageArgumentExecutor(CommandLineOptions.Instance, _runSettingsProvider, new FileHelper()));

set => _executor = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,20 @@ internal class EnableLoggerArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public EnableLoggerArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets or sets the executor.
/// </summary>
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new EnableLoggerArgumentExecutor(RunSettingsManager.Instance));
new EnableLoggerArgumentExecutor(_runSettingsProvider));

set => _executor = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ internal class EnvironmentArgumentProcessor : IArgumentProcessor
public const string CommandName = "/Environment";
private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public EnvironmentArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new ArgumentExecutor(CommandLineOptions.Instance, RunSettingsManager.Instance, ConsoleOutput.Instance));
new ArgumentExecutor(CommandLineOptions.Instance, _runSettingsProvider, ConsoleOutput.Instance));

set => _executor = value;
}
Expand Down
8 changes: 7 additions & 1 deletion src/vstest.console/Processors/FrameworkArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ internal class FrameworkArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public FrameworkArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -41,7 +47,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new FrameworkArgumentExecutor(CommandLineOptions.Instance, RunSettingsManager.Instance));
new FrameworkArgumentExecutor(CommandLineOptions.Instance, _runSettingsProvider));

set => _executor = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ internal class InIsolationArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public InIsolationArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -37,7 +43,7 @@ public Lazy<IArgumentProcessorCapabilities> Metadata
public Lazy<IArgumentExecutor>? Executor
{
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new InIsolationArgumentExecutor(CommandLineOptions.Instance, RunSettingsManager.Instance));
new InIsolationArgumentExecutor(CommandLineOptions.Instance, _runSettingsProvider));

set => _executor = value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ internal class ListFullyQualifiedTestsArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public ListFullyQualifiedTestsArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -48,7 +54,7 @@ public Lazy<IArgumentExecutor>? Executor
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new ListFullyQualifiedTestsArgumentExecutor(
CommandLineOptions.Instance,
RunSettingsManager.Instance,
_runSettingsProvider,
TestRequestManager.Instance));

set => _executor = value;
Expand Down
8 changes: 7 additions & 1 deletion src/vstest.console/Processors/ListTestsArgumentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ internal class ListTestsArgumentProcessor : IArgumentProcessor

private Lazy<IArgumentProcessorCapabilities>? _metadata;
private Lazy<IArgumentExecutor>? _executor;
private readonly IRunSettingsProvider _runSettingsProvider;

public ListTestsArgumentProcessor(IRunSettingsProvider runSettingsProvider)
{
_runSettingsProvider = runSettingsProvider;
}

/// <summary>
/// Gets the metadata.
Expand All @@ -51,7 +57,7 @@ public Lazy<IArgumentExecutor>? Executor
get => _executor ??= new Lazy<IArgumentExecutor>(() =>
new ListTestsArgumentExecutor(
CommandLineOptions.Instance,
RunSettingsManager.Instance,
_runSettingsProvider,
TestRequestManager.Instance));

set => _executor = value;
Expand Down
Loading
Loading