Skip to content

Commit

Permalink
Merge pull request #73 from jakubch1/main
Browse files Browse the repository at this point in the history
Scenario 24
  • Loading branch information
jakubch1 authored Nov 29, 2023
2 parents 6416d5b + 5d1e0de commit e02011e
Show file tree
Hide file tree
Showing 10 changed files with 641 additions and 4 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/Calculator_Scenario24.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: "Calculator Scenario 24"

on:
push:
branches: [ "main" ]
paths: [ 'samples/Calculator/tests/**', 'samples/Calculator/src/**', '.github/workflows/Calculator_Scenario24.yml' ]

jobs:
build:

runs-on: ubuntu-latest
defaults:
run:
working-directory: ./samples/Calculator
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Run tests
run: dotnet test --collect "Code Coverage;Format=Cobertura" --no-build --verbosity normal --results-directory ./TestResults/
working-directory: ./samples/Calculator
- name: ReportGenerator
uses: danielpalme/ReportGenerator-GitHub-Action@5.1.26
with:
reports: '${{ github.workspace }}/samples/Calculator/TestResults/**/*.cobertura.xml'
targetdir: '${{ github.workspace }}/coveragereport'
reporttypes: 'MarkdownSummaryGithub'
- name: Upload coverage into summary
run: cat $GITHUB_WORKSPACE/coveragereport/SummaryGithub.md >> $GITHUB_STEP_SUMMARY
- name: Archive code coverage results
uses: actions/upload-artifact@v3
with:
name: code-coverage-report
path: '${{ github.workspace }}/samples/Calculator/TestResults/**/*.cobertura.xml'
9 changes: 7 additions & 2 deletions docs/instrumentation.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Static and dynamic instrumentation

For years our Microsoft's code coverage tooling was working only on Windows leveraging dynamic instrumentation through CLR profiling ([Profiling Overview - .NET Framework | Microsoft Learn](https://learn.microsoft.com/dotnet/framework/unmanaged-api/profiling/profiling-overview)). Dynamic instrumentation means that libraries are instrumented in memory after library image is loaded by process. In [Microsoft.CodeCoverage](https://www.nuget.org/packages/Microsoft.CodeCoverage) version [17.2.0](https://www.nuget.org/packages/Microsoft.CodeCoverage/17.2.0) we took dependency on [CLR instrumentation engine](https://github.com/microsoft/CLRInstrumentationEngine) and added support for dynamic instrumentation on Linux x64 and macOS x64. Unfortunately we were not able to extend it to all platforms due too some native dependencies not available. Therefore in version [17.5.0](https://www.nuget.org/packages/Microsoft.CodeCoverage/17.5.0) we added option to collect code coverage in static way, which means that libraries are instrumented on disk before loading by the process. As instrumentation is done fully in managed way (leveraging [Mono.Cecil](https://github.com/jbevain/cecil)), it is supported on all platforms where .NET is supported. By default dynamic instrumentation is enabled on all platforms. Static instrumentation is enabled by on non-Windows operating systems. When both dynamic and static instrumentation is enabled our tools detects that file loaded by process is already statically instrumented and skips dynamic instrumentation.
For years, our Microsoft's code coverage tooling was exclusive to Windows, employing dynamic instrumentation through CLR profiling ([Profiling Overview - .NET Framework | Microsoft Learn](https://learn.microsoft.com/dotnet/framework/unmanaged-api/profiling/profiling-overview)). Dynamic instrumentation involves instrumenting libraries in memory after the library image is loaded by the process.

In [Microsoft.CodeCoverage](https://www.nuget.org/packages/Microsoft.CodeCoverage) version [17.2.0](https://www.nuget.org/packages/Microsoft.CodeCoverage/17.2.0), we incorporated a dependency on the [CLR instrumentation engine](https://github.com/microsoft/CLRInstrumentationEngine) and introduced support for dynamic instrumentation on Linux x64 and macOS x64. Unfortunately, extending this support to all platforms was hindered by some unavailable native dependencies.

Therefore, in version [17.5.0](https://www.nuget.org/packages/Microsoft.CodeCoverage/17.5.0), we added an option to collect code coverage in a static way. This implies that libraries are instrumented on disk before loading by the process. As the instrumentation is fully managed (leveraging [Mono.Cecil](https://github.com/jbevain/cecil)), it is supported on all platforms where .NET is available. Dynamic instrumentation remains the default on all platforms, while static instrumentation is enabled on non-Windows operating systems. When both dynamic and static instrumentation are enabled, our tools detect that the file loaded by the process is already statically instrumented and subsequently skip dynamic instrumentation.

Dynamic instrumentation necessitates configuring environment variables (CLR Profiling) for code coverage collection. In environments where this isn't feasible, such as in IIS, we recommend opting for static instrumentation.

Dynamic instrumentation requires configuring environment variables (CLR Profiling) to collect code coverage. In environments where this is not possible (for example IIS) we recommend using static instrumentation.

You can control dynamic and static instrumentation using below flags in your configuration:
```xml
Expand Down
2 changes: 1 addition & 1 deletion docs/supported-os.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Supported OS versions

Table below shows availability of our tooling on different operating systems. It also specifies if particular instrumentation is enabled or disabled by default.
Below table illustrates the availability of our tooling on different operating systems and specifies whether dynamic or static instrumentation is enabled or disabled by default.

OS | Version | Architectures | Dynamic instrumentation |Static instrumentation
--------------------------------------|-----------------------|-------------------|-------------------------|----------------------
Expand Down
3 changes: 2 additions & 1 deletion samples/Calculator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ Solution contains seven projects:
20. [***Scenario 20*** Code coverage for whole solution with logs](scenarios/scenario20/README.md)
21. [***Scenario 21*** Code coverage for whole solution with script to run tests](scenarios/scenario21/README.md)
22. [***Scenario 22*** Code coverage for child processes using static instrumentation without binaries restore](scenarios/scenario22/README.md)
23. [***Scenario 23*** Code coverage for child processes using static instrumentation with binaries restore](scenarios/scenario23/README.md)
23. [***Scenario 23*** Code coverage for child processes using static instrumentation with binaries restore](scenarios/scenario23/README.md)
24. [***Scenario 24*** Code coverage for whole solution (only unit tests)](scenarios/scenario24/README.md)
87 changes: 87 additions & 0 deletions samples/Calculator/scenarios/scenario24/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Scenario Description

Gather code coverage for the entire solution. Refer to the provided example for collecting coverage across all unit tests. The system automatically consolidates coverage reports from all test projects. Integration tests are excluded for simplification, as the server is not initiated during this process.

# Collect code coverage using command line

```shell
git clone https://github.com/microsoft/codecoverage.git
cd codecoverage/samples/Calculator
dotnet build
dotnet test --no-build --collect "Code Coverage"
```

You can also use [run.ps1](run.ps1) to collect code coverage.

# Collect code coverage inside github workflow

Executing tests is automatically creating `cobertura` report. Then `reportgenerator` can be used to generate final github summary markdown.

```yml
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Run tests
run: dotnet test --collect "Code Coverage;Format=Cobertura" --no-build --verbosity normal --results-directory ./TestResults/
working-directory: ./samples/Calculator
- name: ReportGenerator
uses: danielpalme/ReportGenerator-GitHub-Action@5.1.26
with:
reports: '${{ github.workspace }}/samples/Calculator/TestResults/**/*.cobertura.xml'
targetdir: '${{ github.workspace }}/coveragereport'
reporttypes: 'MarkdownSummaryGithub'
- name: Upload coverage into summary
run: cat $GITHUB_WORKSPACE/coveragereport/SummaryGithub.md >> $GITHUB_STEP_SUMMARY
- name: Archive code coverage results
uses: actions/upload-artifact@v3
with:
name: code-coverage-report
path: '${{ github.workspace }}/samples/Calculator/TestResults/**/*.cobertura.xml'
```
[Full source example](../../../../.github/workflows/Calculator_Scenario24.yml)
[Run example](../../../../../../actions/workflows/Calculator_Scenario24.yml)
# Collect code coverage inside Azure DevOps Pipelines
```yml
steps:
- task: DotNetCoreCLI@2
inputs:
command: 'restore'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'dotnet restore'

- task: DotNetCoreCLI@2
inputs:
command: 'build'
arguments: '--no-restore --configuration $(buildConfiguration)'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'dotnet build'

- task: DotNetCoreCLI@2
inputs:
command: 'test'
arguments: '--no-build --configuration $(buildConfiguration) --collect "Code Coverage"'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'execute tests'
```
> **_NOTE:_** Azure DevOps pipelines automatically recognize binary coverage report format. Code coverage results are automatically processed and published to Azure DevOps. No additional steps needed.
[Full source example](azure-pipelines.yml)
![alt text](azure-pipelines.jpg "Code Coverage tab in Azure DevOps pipelines")
# Report example
![alt text](example.report.jpg "Example report")
[Link](example.report.cobertura.xml)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions samples/Calculator/scenarios/scenario24/azure-pipelines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Calculator Scenario 24"

pool:
vmImage: 'ubuntu-latest'

variables:
buildConfiguration: 'Debug'
solutionPath: './samples/Calculator/Calculator.sln' # this is specific to example - in most cases not needed

steps:
- task: DotNetCoreCLI@2
inputs:
command: 'restore'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'dotnet restore'

- task: DotNetCoreCLI@2
inputs:
command: 'build'
arguments: '--no-restore --configuration $(buildConfiguration)'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'dotnet build'

- task: DotNetCoreCLI@2
inputs:
command: 'test'
arguments: '--no-build --configuration $(buildConfiguration) --collect "Code Coverage"'
projects: '$(solutionPath)' # this is specific to example - in most cases not needed
displayName: 'execute tests'
Loading

0 comments on commit e02011e

Please sign in to comment.