Skip to content
Draft
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
237 changes: 237 additions & 0 deletions .github/skills/settings-migration/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
---
name: settings-migration
description: Migrate legacy Visual Studio .testsettings files to .runsettings format. Use when users need to convert old test configuration files, understand the mapping between formats, or troubleshoot migration issues. Replaces the deprecated SettingsMigrator.exe tool.
---

# Migrating .testsettings to .runsettings

This skill replaces the deprecated `SettingsMigrator.exe` tool. It covers migrating legacy `.testsettings` files (and `.runsettings` files with embedded `.testsettings` references) to the modern `.runsettings` format.

## Background

Visual Studio historically used `.testsettings` files for test configuration. The modern format is `.runsettings`. The two formats differ in structure, and some `.testsettings` nodes map to `<LegacySettings>` wrappers in `.runsettings` to preserve backward compatibility.

See also: [RFC 0023 - TestSettings Deprecation](../../docs/RFCs/0023-TestSettings-Deprecation.md)
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The relative link likely points to .github/docs/... rather than the repo-level docs/... because this file lives under .github/skills/settings-migration/. Update the relative path (e.g., one more ..) so the RFC link resolves correctly on GitHub.

Suggested change
See also: [RFC 0023 - TestSettings Deprecation](../../docs/RFCs/0023-TestSettings-Deprecation.md)
See also: [RFC 0023 - TestSettings Deprecation](../../../docs/RFCs/0023-TestSettings-Deprecation.md)

Copilot uses AI. Check for mistakes.

## When to Use

- User has a `.testsettings` file and needs a `.runsettings` file
- User has a `.runsettings` with an `<MSTest><SettingsFile>path.testsettings</SettingsFile></MSTest>` embedded reference
- User asks about migrating test settings, test configuration formats, or legacy settings

## Step-by-Step Migration

### Step 1: Identify the Source File Type

Check the file extension:
- **`.testsettings`** → Go to [Migrate from .testsettings](#migrate-from-testsettings)
- **`.runsettings` with embedded settings** → Go to [Migrate embedded .testsettings](#migrate-embedded-testsettings-from-runsettings)

To check for embedded settings in a `.runsettings` file, look for:
```xml
<RunSettings>
<MSTest>
<SettingsFile>path\to\file.testsettings</SettingsFile>
</MSTest>
</RunSettings>
```

### Step 2: Read the .testsettings File

Parse the `.testsettings` XML and extract these nodes (all are optional):

| XPath in .testsettings | Node Name | Purpose |
|---|---|---|
| `/TestSettings/Deployment` | Deployment | File deployment settings |
| `/TestSettings/Scripts` | Scripts | Setup/cleanup scripts |
| `/TestSettings/Execution/TestTypeSpecific/WebTestRunConfiguration` | WebSettings | Web test configuration |
| `/TestSettings/Execution/AgentRule/DataCollectors/DataCollector` | DataCollectors | Data collector definitions |
| `/TestSettings/Execution/Timeouts` | Timeouts | Timeout attributes |
| `/TestSettings/Execution/TestTypeSpecific/UnitTestRunConfig` | UnitTestConfig | Unit test run configuration |
| `/TestSettings/Execution/Hosts` | Hosts | Host configuration |
| `/TestSettings/Execution` | Execution | Execution attributes (parallelTestCount, hostProcessPlatform) |
Comment on lines +43 to +52
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

This markdown table syntax is invalid due to the double leading pipes (||), which prevents GitHub from rendering it as a table. Replace each row with a single leading | so the mapping table renders correctly.

Copilot uses AI. Check for mistakes.

### Step 3: Build the .runsettings Output

Start with a minimal `.runsettings` skeleton:

```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
</RunSettings>
```

Then apply each mapping rule below.

#### 3a. WebTestRunConfiguration (top-level)

If the `.testsettings` has a `WebTestRunConfiguration` node, copy it directly as a child of `<RunSettings>`:

```xml
<RunSettings>
<WebTestRunConfiguration>
<!-- copied from .testsettings as-is -->
</WebTestRunConfiguration>
</RunSettings>
```

#### 3b. LegacySettings Node

If **any** of these are present: `Deployment`, `Scripts`, `UnitTestConfig`, `Hosts`, `parallelTestCount`, `testTimeout`, or `hostProcessPlatform`, create a `<LegacySettings>` node and also set `<MSTest><ForcedLegacyMode>true</ForcedLegacyMode></MSTest>`:
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

The rules are internally inconsistent for hostProcessPlatform: it is listed as a trigger for creating <LegacySettings>, and it is described as an attribute on <Execution>, but the condition for creating <Execution> does not include hostProcessPlatform. As written, a .testsettings with only hostProcessPlatform would have no documented destination for that value. Please update the <Execution> creation rule to include hostProcessPlatform (or explicitly document that hostProcessPlatform is only migrated when some other <Execution> trigger is present).

Copilot uses AI. Check for mistakes.

```xml
<RunSettings>
<LegacySettings>
<!-- Deployment node (copied as-is) -->
<Deployment ... />

<!-- Scripts node (copied as-is) -->
<Scripts ... />

<!-- Execution node (built from parts) -->
<Execution parallelTestCount="N" hostProcessPlatform="X">
<Timeouts testTimeout="M" />
<Hosts>...</Hosts>
<TestTypeSpecific>
<UnitTestRunConfig>...</UnitTestRunConfig>
</TestTypeSpecific>
</Execution>
</LegacySettings>

<MSTest>
<ForcedLegacyMode>true</ForcedLegacyMode>
</MSTest>
</RunSettings>
```

**Rules for the Execution sub-node:**
- Only create `<Execution>` if at least one of `UnitTestConfig`, `parallelTestCount`, `testTimeout`, `Hosts` is present
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

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

The rules are internally inconsistent: the bullet list says hostProcessPlatform is an <Execution> attribute, but the condition for creating <Execution> doesn’t include hostProcessPlatform. Either include hostProcessPlatform in the creation condition, or explicitly document what to do when hostProcessPlatform is present but none of the other triggers are.

Suggested change
- Only create `<Execution>` if at least one of `UnitTestConfig`, `parallelTestCount`, `testTimeout`, `Hosts` is present
- Only create `<Execution>` if at least one of `UnitTestConfig`, `parallelTestCount`, `testTimeout`, `Hosts`, or `hostProcessPlatform` is present

Copilot uses AI. Check for mistakes.
- `parallelTestCount` and `hostProcessPlatform` are **attributes** on the `<Execution>` element
- `testTimeout` goes into `<Timeouts testTimeout="..."/>` child element
- `Hosts` is copied as a child element
- `UnitTestRunConfig` is wrapped in `<TestTypeSpecific>` child element

#### 3c. TestSessionTimeout (from runTimeout)

If the `.testsettings` `Timeouts` node has a `runTimeout` attribute, map it to:

```xml
<RunSettings>
<RunConfiguration>
<TestSessionTimeout>VALUE</TestSessionTimeout>
</RunConfiguration>
</RunSettings>
```

#### 3d. DataCollectors

If the `.testsettings` has DataCollector nodes, copy them to:

```xml
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector ...>...</DataCollector>
<!-- each DataCollector copied as-is -->
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
```

#### 3e. Unsupported Attributes (warn the user)

These `.testsettings` timeout attributes are **not supported** and will be lost:
- `agentNotRespondingTimeout`
- `deploymentTimeout`
- `scriptTimeout`

If any of these are present, warn the user that they cannot be migrated.

### Migrate from .testsettings

1. Parse the `.testsettings` file (Step 2)
2. Build a fresh `.runsettings` (Step 3)
3. Save the output file

### Migrate Embedded .testsettings from .runsettings

1. Parse the existing `.runsettings` file
2. Find `<MSTest><SettingsFile>` node and read the `.testsettings` path
- If the path is relative, resolve it relative to the `.runsettings` file location
3. Remove the `<SettingsFile>` node from the `.runsettings`
4. Parse the referenced `.testsettings` file (Step 2)
5. Apply all mappings (Step 3) into the existing `.runsettings` document
6. If there's already a `<LegacySettings>` node in the `.runsettings`, remove it first (it will be replaced)
7. Save the output file

## Complete Example

### Input: `legacy.testsettings`

```xml
<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="MySettings">
<Deployment>
<DeploymentItem filename="TestData\" />
</Deployment>
<Scripts setupScript="setup.bat" cleanupScript="cleanup.bat" />
<Execution parallelTestCount="4">
<Timeouts testTimeout="30000" runTimeout="600000" agentNotRespondingTimeout="300000" />
<TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution>
<TestDirectory useLoadContext="true" />
</AssemblyResolution>
</UnitTestRunConfig>
</TestTypeSpecific>
<Hosts />
</Execution>
<AgentRule name="LocalMachineDefaultRole">
<DataCollectors>
<DataCollector uri="datacollector://Microsoft/CodeCoverage/2.0"
assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector">
</DataCollector>
</DataCollectors>
</AgentRule>
</TestSettings>
```

### Output: `migrated.runsettings`

```xml
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<LegacySettings>
<Deployment>
<DeploymentItem filename="TestData\" />
</Deployment>
<Scripts setupScript="setup.bat" cleanupScript="cleanup.bat" />
<Execution parallelTestCount="4">
<Timeouts testTimeout="30000" />
<Hosts />
<TestTypeSpecific>
<UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
<AssemblyResolution>
<TestDirectory useLoadContext="true" />
</AssemblyResolution>
</UnitTestRunConfig>
</TestTypeSpecific>
</Execution>
</LegacySettings>
<RunConfiguration>
<TestSessionTimeout>600000</TestSessionTimeout>
</RunConfiguration>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector uri="datacollector://Microsoft/CodeCoverage/2.0"
assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector">
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
<MSTest>
<ForcedLegacyMode>true</ForcedLegacyMode>
</MSTest>
</RunSettings>
```

> **Note:** The `agentNotRespondingTimeout` attribute was dropped because it is not supported in the modern format. The user should be warned about this.
14 changes: 0 additions & 14 deletions TestPlatform.sln
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataCollectors", "DataColle
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests", "test\DataCollectors\Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests\Microsoft.TestPlatform.Extensions.EventLogCollector.UnitTests.csproj", "{21DB138B-85B7-479E-91FE-01E0F972EC56}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsMigrator", "src\SettingsMigrator\SettingsMigrator.csproj", "{69F5FF81-5615-4F06-B83C-FCF979BB84CA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsMigrator.UnitTests", "test\SettingsMigrator.UnitTests\SettingsMigrator.UnitTests.csproj", "{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.HtmlLogger", "src\Microsoft.TestPlatform.Extensions.HtmlLogger\Microsoft.TestPlatform.Extensions.HtmlLogger.csproj", "{236A71E3-01DA-4679-9DFF-16A8E079ACFF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests", "test\Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests\Microsoft.TestPlatform.Extensions.HtmlLogger.UnitTests.csproj", "{41248B96-6E15-4E5E-A78F-859897676814}"
Expand Down Expand Up @@ -352,14 +348,6 @@ Global
{21DB138B-85B7-479E-91FE-01E0F972EC56}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21DB138B-85B7-479E-91FE-01E0F972EC56}.Release|Any CPU.Build.0 = Release|Any CPU
{69F5FF81-5615-4F06-B83C-FCF979BB84CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69F5FF81-5615-4F06-B83C-FCF979BB84CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69F5FF81-5615-4F06-B83C-FCF979BB84CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69F5FF81-5615-4F06-B83C-FCF979BB84CA}.Release|Any CPU.Build.0 = Release|Any CPU
{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E}.Release|Any CPU.Build.0 = Release|Any CPU
{236A71E3-01DA-4679-9DFF-16A8E079ACFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{236A71E3-01DA-4679-9DFF-16A8E079ACFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{236A71E3-01DA-4679-9DFF-16A8E079ACFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -526,8 +514,6 @@ Global
{65A25D6E-C9CC-4F45-8925-04087AC82634} = {B705537C-B82C-4A30-AFA5-6244D9A7DAEB}
{D9A30E32-D466-4EC5-B4F2-62E17562279B} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6}
{21DB138B-85B7-479E-91FE-01E0F972EC56} = {D9A30E32-D466-4EC5-B4F2-62E17562279B}
{69F5FF81-5615-4F06-B83C-FCF979BB84CA} = {ED0C35EB-7F31-4841-A24F-8EB708FFA959}
{E7D4921C-F12D-4E1C-85AC-8B7F91C59B0E} = {B27FAFDF-2DBA-4AB0-BA85-FD5F21D359D6}
{236A71E3-01DA-4679-9DFF-16A8E079ACFF} = {5E7F18A8-F843-4C8A-AB02-4C7D9205C6CF}
{41248B96-6E15-4E5E-A78F-859897676814} = {020E15EA-731F-4667-95AF-226671E0C3AE}
{074F5BD6-DC05-460B-B78F-044D125FD787} = {D9A30E32-D466-4EC5-B4F2-62E17562279B}
Expand Down
Loading
Loading