Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6c3b9d4
.NET: Upgrade to XUnit 3 and Microsoft Testing Platform (#4176)
westey-m Feb 24, 2026
d04228d
Fix copilot studio integration tests failure (#4209)
westey-m Feb 24, 2026
fb73231
Fix anthropic integration tests and skip reason (#4211)
westey-m Feb 24, 2026
be5ed93
Remove accidental add of code coverage for integration tests (#4219)
westey-m Feb 24, 2026
a39e324
Add solution filtered parallel test run (#4226)
westey-m Feb 24, 2026
f1585b1
Fix build paths (#4228)
westey-m Feb 24, 2026
9e3d657
Fix coverage settings path and trait filter (#4229)
westey-m Feb 24, 2026
b8a5912
Add project name filter to solution (#4231)
westey-m Feb 24, 2026
059398c
Increase Integration Test Parallelism (#4241)
westey-m Feb 25, 2026
0ee09d3
Increase integration tests threads to 4x (#4242)
westey-m Feb 25, 2026
9059721
Separate build and test into parallel jobs (#4243)
westey-m Feb 25, 2026
ce9f2cd
Filter src by framework for tests build (#4244)
westey-m Feb 25, 2026
ce0b374
Pre-build samples via tests to avoid timeouts (#4245)
westey-m Feb 25, 2026
9c9168f
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Feb 25, 2026
17f75c3
Separate build from run for console sample validation (#4251)
westey-m Feb 25, 2026
d4f9579
Address PR comments (#4255)
westey-m Feb 25, 2026
cc1ef73
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Feb 25, 2026
8b191de
Merge and move scripts (#4308)
westey-m Feb 26, 2026
204d01c
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Feb 26, 2026
6bc5f54
Fix encoding (#4309)
westey-m Feb 26, 2026
279386d
Disable Parallelization for WorkflowRunActivityStopTests (#4313)
westey-m Feb 26, 2026
7cb4e78
Revert parallel disable (#4324)
westey-m Feb 26, 2026
5dcf45b
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 3, 2026
21e7873
.NET: Disable flakey Workflow Observability tests (#4416)
westey-m Mar 3, 2026
213fcb9
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 3, 2026
e730fc1
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 4, 2026
5441fec
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 4, 2026
9203cbe
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 4, 2026
410b68e
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 4, 2026
158e418
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 5, 2026
0e13d18
Merge branch 'main' into feature-xunit3-mtp-upgrade
westey-m Mar 5, 2026
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
145 changes: 145 additions & 0 deletions .github/workflows/New-FilteredSolution.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/usr/bin/env pwsh
# Copyright (c) Microsoft. All rights reserved.

<#
.SYNOPSIS
Generates a filtered .slnx solution file by removing projects that don't match the specified criteria.

.DESCRIPTION
Parses a .slnx solution file and applies one or more filters:
- Removes projects that don't support the specified target framework (via MSBuild query).
- Optionally removes all sample projects (under samples/).
- Optionally filters test projects by name pattern (e.g., only *UnitTests*).
Writes the filtered solution to the specified output path and prints the path.

.PARAMETER Solution
Path to the source .slnx solution file.

.PARAMETER TargetFramework
The target framework to filter by (e.g., net10.0, net472).

.PARAMETER Configuration
Optional MSBuild configuration used when querying TargetFrameworks. Defaults to Debug.

.PARAMETER TestProjectNameFilter
Optional wildcard pattern to filter test project names (e.g., *UnitTests*, *IntegrationTests*).
When specified, only test projects whose filename matches this pattern are kept.

.PARAMETER ExcludeSamples
When specified, removes all projects under the samples/ directory from the solution.

.PARAMETER OutputPath
Optional output path for the filtered .slnx file. If not specified, a temp file is created.

.EXAMPLE
# Generate a filtered solution and run tests
$filtered = .github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472
dotnet test --solution $filtered --no-build -f net472

.EXAMPLE
# Generate a solution with only unit test projects
.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net10.0 -TestProjectNameFilter "*UnitTests*" -OutputPath filtered-unit.slnx

.EXAMPLE
# Inline usage with dotnet test (PowerShell)
dotnet test --solution (.github/workflows/New-FilteredSolution.ps1 -Solution dotnet/agent-framework-dotnet.slnx -TargetFramework net472) --no-build -f net472
#>

[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Solution,

[Parameter(Mandatory)]
[string]$TargetFramework,

[string]$Configuration = "Debug",

[string]$TestProjectNameFilter,

[switch]$ExcludeSamples,

[string]$OutputPath
)

$ErrorActionPreference = "Stop"

# Resolve the solution path
$solutionPath = Resolve-Path $Solution
$solutionDir = Split-Path $solutionPath -Parent

if (-not $OutputPath) {
$OutputPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "filtered-$(Split-Path $solutionPath -Leaf)")
}

# Parse the .slnx XML
[xml]$slnx = Get-Content $solutionPath -Raw

$removed = @()
$kept = @()

# Remove sample projects if requested
if ($ExcludeSamples) {
$sampleProjects = $slnx.SelectNodes("//Project[contains(@Path, 'samples/')]")
foreach ($proj in $sampleProjects) {
$projRelPath = $proj.GetAttribute("Path")
Write-Verbose "Removing (sample): $projRelPath"
$removed += $projRelPath
$proj.ParentNode.RemoveChild($proj) | Out-Null
}
Write-Host "Removed $($sampleProjects.Count) sample project(s)." -ForegroundColor Yellow
}

# Filter all remaining projects by target framework
$allProjects = $slnx.SelectNodes("//Project")

foreach ($proj in $allProjects) {
$projRelPath = $proj.GetAttribute("Path")
$projFullPath = Join-Path $solutionDir $projRelPath
$projFileName = Split-Path $projRelPath -Leaf
$isTestProject = $projRelPath -like "*tests/*"

# Filter test projects by name pattern if specified
if ($isTestProject -and $TestProjectNameFilter -and ($projFileName -notlike $TestProjectNameFilter)) {
Write-Verbose "Removing (name filter): $projRelPath"
$removed += $projRelPath
$proj.ParentNode.RemoveChild($proj) | Out-Null
continue
}

if (-not (Test-Path $projFullPath)) {
Write-Verbose "Project not found, keeping in solution: $projRelPath"
$kept += $projRelPath
continue
}

# Query the project's target frameworks using MSBuild
$targetFrameworks = & dotnet msbuild $projFullPath -getProperty:TargetFrameworks -p:Configuration=$Configuration -nologo 2>$null
$targetFrameworks = $targetFrameworks.Trim()

if ($targetFrameworks -like "*$TargetFramework*") {
Write-Verbose "Keeping: $projRelPath (targets: $targetFrameworks)"
$kept += $projRelPath
}
else {
Write-Verbose "Removing: $projRelPath (targets: $targetFrameworks, missing: $TargetFramework)"
$removed += $projRelPath
$proj.ParentNode.RemoveChild($proj) | Out-Null
}
}

# Write the filtered solution
$slnx.Save($OutputPath)

# Report results to stderr so stdout is clean for piping
Write-Host "Filtered solution written to: $OutputPath" -ForegroundColor Green
if ($removed.Count -gt 0) {
Write-Host "Removed $($removed.Count) project(s):" -ForegroundColor Yellow
foreach ($r in $removed) {
Write-Host " - $r" -ForegroundColor Yellow
}
}
Write-Host "Kept $($kept.Count) project(s)." -ForegroundColor Green

# Output the path for piping
Write-Output $OutputPath
153 changes: 107 additions & 46 deletions .github/workflows/dotnet-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ jobs:
if: steps.filter.outputs.dotnet != 'true'
run: echo "NOT dotnet file"

dotnet-build-and-test:
# Build the full solution (including samples) on all TFMs. No tests.
dotnet-build:
needs: paths-filter
if: needs.paths-filter.outputs.dotnetChanges == 'true'
strategy:
fail-fast: false
matrix:
include:
- { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" }
- { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release }
- { targetFramework: "net9.0", os: "windows-latest", configuration: Debug }
- { targetFramework: "net8.0", os: "ubuntu-latest", configuration: Release }
- { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" }
- { targetFramework: "net472", os: "windows-latest", configuration: Release }

runs-on: ${{ matrix.os }}
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -84,16 +84,6 @@ jobs:
python
workflow-samples

# Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened)
- name: Start Azure Cosmos DB Emulator
if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }}
shell: pwsh
run: |
Write-Host "Launching Azure Cosmos DB Emulator"
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV

- name: Setup dotnet
uses: actions/setup-dotnet@v5.1.0
with:
Expand Down Expand Up @@ -140,25 +130,98 @@ jobs:
popd
rm -rf "$TEMP_DIR"

- name: Run Unit Tests
# Build src+tests only (no samples) for a single TFM and run tests.
dotnet-test:
needs: paths-filter
if: needs.paths-filter.outputs.dotnetChanges == 'true'
strategy:
fail-fast: false
matrix:
include:
- { targetFramework: "net10.0", os: "ubuntu-latest", configuration: Release, integration-tests: true, environment: "integration" }
- { targetFramework: "net472", os: "windows-latest", configuration: Release, integration-tests: true, environment: "integration" }

runs-on: ${{ matrix.os }}
environment: ${{ matrix.environment }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
sparse-checkout: |
.
.github
dotnet
python
workflow-samples

# Start Cosmos DB Emulator for all integration tests and only for unit tests when CosmosDB changes happened)
- name: Start Azure Cosmos DB Emulator
if: ${{ runner.os == 'Windows' && (needs.paths-filter.outputs.cosmosDbChanges == 'true' || (github.event_name != 'pull_request' && matrix.integration-tests)) }}
shell: pwsh
run: |
Write-Host "Launching Azure Cosmos DB Emulator"
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
Start-CosmosDbEmulator -NoUI -Key "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
echo "COSMOS_EMULATOR_AVAILABLE=true" >> $env:GITHUB_ENV

- name: Setup dotnet
uses: actions/setup-dotnet@v5.1.0
with:
global-json-file: ${{ github.workspace }}/dotnet/global.json

- name: Generate test solution (no samples)
shell: pwsh
run: |
.github/workflows/New-FilteredSolution.ps1 `
-Solution dotnet/agent-framework-dotnet.slnx `
-TargetFramework ${{ matrix.targetFramework }} `
-Configuration ${{ matrix.configuration }} `
-ExcludeSamples `
-OutputPath dotnet/filtered.slnx `
-Verbose

- name: Build src and tests
shell: bash
run: dotnet build dotnet/filtered.slnx -c ${{ matrix.configuration }} -f ${{ matrix.targetFramework }} --warnaserror

- name: Generate test-type filtered solutions
shell: pwsh
run: |
export UT_PROJECTS=$(find ./dotnet -type f -name "*.UnitTests.csproj" | tr '\n' ' ')
for project in $UT_PROJECTS; do
# Query the project's target frameworks using MSBuild with the current configuration
target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r')

# Check if the project supports the target framework
if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then
if [[ "${{ matrix.targetFramework }}" == "${{ env.COVERAGE_FRAMEWORK }}" ]]; then
dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --collect:"XPlat Code Coverage" --results-directory:"TestResults/Coverage/" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.ExcludeByAttribute=GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute
else
dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx
fi
else
echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)"
fi
done
$commonArgs = @{
Solution = "dotnet/filtered.slnx"
TargetFramework = "${{ matrix.targetFramework }}"
Configuration = "${{ matrix.configuration }}"
Verbose = $true
}
.github/workflows/New-FilteredSolution.ps1 @commonArgs `
-TestProjectNameFilter "*UnitTests*" `
-OutputPath dotnet/filtered-unit.slnx
.github/workflows/New-FilteredSolution.ps1 @commonArgs `
-TestProjectNameFilter "*IntegrationTests*" `
-OutputPath dotnet/filtered-integration.slnx

- name: Run Unit Tests
shell: pwsh
working-directory: dotnet
run: |
$coverageSettings = Join-Path $PWD "tests/coverage.runsettings"
$coverageArgs = @()
if ("${{ matrix.targetFramework }}" -eq "${{ env.COVERAGE_FRAMEWORK }}") {
$coverageArgs = @(
"--coverage",
"--coverage-output-format", "cobertura",
"--coverage-settings", $coverageSettings,
"--results-directory", "../TestResults/Coverage/"
)
}

dotnet test --solution ./filtered-unit.slnx `
-f ${{ matrix.targetFramework }} `
-c ${{ matrix.configuration }} `
--no-build -v Normal `
--report-xunit-trx `
--ignore-exit-code 8 `
@coverageArgs
env:
# Cosmos DB Emulator connection settings
COSMOSDB_ENDPOINT: https://localhost:8081
Expand All @@ -185,21 +248,19 @@ jobs:
id: azure-functions-setup

- name: Run Integration Tests
shell: bash
shell: pwsh
working-directory: dotnet
if: github.event_name != 'pull_request' && matrix.integration-tests
run: |
export INTEGRATION_TEST_PROJECTS=$(find ./dotnet -type f -name "*IntegrationTests.csproj" | tr '\n' ' ')
for project in $INTEGRATION_TEST_PROJECTS; do
# Query the project's target frameworks using MSBuild with the current configuration
target_frameworks=$(dotnet msbuild $project -getProperty:TargetFrameworks -p:Configuration=${{ matrix.configuration }} -nologo 2>/dev/null | tr -d '\r')

# Check if the project supports the target framework
if [[ "$target_frameworks" == *"${{ matrix.targetFramework }}"* ]]; then
dotnet test -f ${{ matrix.targetFramework }} -c ${{ matrix.configuration }} $project --no-build -v Normal --logger trx --filter "Category!=IntegrationDisabled"
else
echo "Skipping $project - does not support target framework ${{ matrix.targetFramework }} (supports: $target_frameworks)"
fi
done
dotnet test --solution ./filtered-integration.slnx `
-f ${{ matrix.targetFramework }} `
-c ${{ matrix.configuration }} `
--no-build -v Normal `
--report-xunit-trx `
--ignore-exit-code 8 `
--filter-not-trait "Category=IntegrationDisabled" `
--parallel-algorithm aggressive `
--max-threads 2.0x
env:
# Cosmos DB Emulator connection settings
COSMOSDB_ENDPOINT: https://localhost:8081
Expand All @@ -225,7 +286,7 @@ jobs:
if: matrix.targetFramework == env.COVERAGE_FRAMEWORK
uses: danielpalme/ReportGenerator-GitHub-Action@5.5.1
with:
reports: "./TestResults/Coverage/**/coverage.cobertura.xml"
reports: "./TestResults/Coverage/**/*.cobertura.xml"
targetdir: "./TestResults/Reports"
reporttypes: "HtmlInline;JsonSummary"

Expand All @@ -245,7 +306,7 @@ jobs:
dotnet-build-and-test-check:
if: always()
runs-on: ubuntu-latest
needs: [dotnet-build-and-test]
needs: [dotnet-build, dotnet-test]
steps:
- name: Get Date
shell: bash
Expand Down
Loading
Loading