---
url: /tutorial/setting-up-tga-dotnet.md
---

# Setting up Test Gap Analysis on a .NET Core Application (Blogifier)

As a developer or tester of a .NET application, you may use Teamscale's [Test Gap Analysis (TGA)](../../glossary/#test-gap-analysis) to improve your testing effectiveness.
This tutorial leads you through the setup of TGA on [Blogifier][blogifier], an open source web application that can be used to self-host a blog.

\[\[toc]]

## Step 1: Installing Blogifier

As a prerequisite for running Blogifier, install [.NET Core Runtime 6.0 (or newer)](https://dotnet.microsoft.com/en-us/download).

Next, download and unzip the [3.0 release][blogifier-3.0-release] of Blogifier.
Note that the release zip contains not only the executable, but also the corresponding [PDB (symbol) files](https://learn.microsoft.com/en-us/visualstudio/debugger/specify-symbol-dot-pdb-and-source-files-in-the-visual-studio-debugger).

Finally, start Blogifier.exe and [open it in your browser](http://127.0.0.1:5000), to ensure that the installation was successful.

## Step 2: Installing the Teamscale .NET Profiler

Download [the latest release of the Teamscale .NET Profiler][dotnet-releases] and unpack it to `C:\teamscale_dotnet_profiler`.

To register the profiler, multiple environment variables have to be set.
This can be accomplished either by creating a launch script, or by assigning the environment variables system-wide in the Control Panel.
For example you can create the `Blogifier.bat` batch file next to the `Blogifier.exe` that sets the necessary environment variables and starts the executable:

```
set CORECLR_ENABLE_PROFILING=1
set CORECLR_PROFILER={DD0A1BB6-11CE-11DD-8EE8-3F9E55D89593}
set CORECLR_PROFILER_PATH_32=C:\teamscale_dotnet_profiler\Profiler32.dll
set CORECLR_PROFILER_PATH_64=C:\teamscale_dotnet_profiler\Profiler64.dll
Blogifier.exe
```

:::warning Workaround for .NET Core profiling in version <= v23.6.0
Due to a bug in the older versions of the Teamscale .NET Profiler, it requires the setting of one more environment variable: `set COR_PROFILER_PATH=C:\teamscale_dotnet_profiler\Profiler64.dll`
:::

If you want to set the environment variables system-wide, you can do that by navigating to *Control Panel* > *System* > *Advanced system settings* > *Environment Variables* and adding all the `CORECLR_` variables.

Setting the `CORECLR_ENABLE_PROFILING` variable enables profiling on .NET Core processes.
The `CORECLR_PROFILER` variable provides the GUID of the Teamscale .NET Profiler, which the .NET Runtime uses to verify the profiler DLL on load.
Finally, the `CORECLR_PROFILER_PATH_32` and `CORECLR_PROFILER_PATH_64` tell the .NET Runtime where to find the profiler binaries.

:::tip Environment variables depend on the .NET version of the application
Note that Blogifier is a .NET **Core** application.
If you had a .NET **Framework** application, the prefix of the environment variables must be `COR_` instead of `CORECLR_`.
:::

Next, we configure the profiler itself.
Rename `C:\teamscale_dotnet_profiler\Profiler.example.yml` to `C:\teamscale_dotnet_profiler\Profiler.yml`, then edit the file.

Adjust the global configuration section, i.e., the first entry below `match`, as follows:

```yaml{9-11}
{
  # ...
  match: [
    {
    # This section matches all processes.
    # We configure shared options here but disable the profiler.
    # The later match sections will enable the profiler for specific processes.
      profiler: {
        enabled: false,
        targetdir: "C:\\blogifier_coverage",
        upload_daemon: false
      },
    # ...
    }
  ]
}
```

This globally disables both the profiler, to avoid profiling unnecessary processes, and the automatic upload of coverage, which we will configure later.
Moreover, it instructs the profiler to write trace files to `C:\blogifier_coverage`.

:::warning Permissions
Ensure that `C:\blogifier_coverage` exists and that the user running `Blogifier.exe` can write to it.
Otherwise, profiling the process will fail.
:::

Next, add a process-specific configuration section below the global one (delete all other sections from the template), to enable the profiler specifically for the Blogifier executable:

```yaml{9-14}
{
  # ...
  match: [
    {
    # This section matches all processes.
    # ...
    },
    # These sections turn on the profiler for specific executables.
    {
      executableName: "Blogifier.exe",
      profiler: {
        enabled: true
      }
    }
  ]
}
```

To test your setup, start Blogifier with the batch file you have created in Step 2.
This will open a console window that shows the logs of the web application, and will start the application on the [default 5000 port](http://127.0.0.1:5000).
Open the application in the browser, do something (e.g., create an admin user), and then stop Blogifier by pressing Ctrl+C in the console window.
The target directory `C:\blogifier_coverage` should now contain a coverage report named like `coverage_20230629_1352510131.txt` (the date and time parts of the file name will differ) with some content.

:::tip Reduce Performance Impact
Profiling processes always incurs a performance impact on the processes.
The Teamscale .NET Profiler minimizes this impact, but cannot completely avoid it.
To minimize the overall impact, ensure you always **configure the minimal set of processes** (executables).
:::

:::tip Troubleshooting For Missing Coverages
If you cannot find the coverage files in the target directory, the root cause is typically one of these reasons:

* The profiler was not attached to your application.
  For this, you can check the log file at `C:\teamscale_dotnet_profiler\attach.log`.
  The profiler creates this file and always adds a new entry when it is attached.
  If there is no new entry, check if the profiler is enabled for your executable in the `Profiler.yml` file.
* The `Profiler.yml` configuration was not found by the profiler.
  In this case the profiler creates the coverage files at `C:\Users\Public` by default.
  To solve the issue, ensure that the `Profiler.yml` file is in the same folder as the profiler DLLs.
  You can also provide a custom path to your configuration file via the environment variable `COR_PROFILER_CONFIG`.
* The `Profiler.yml` configuration file is syntactically incorrect.
  In this case in newer versions of the profiler the error is shown and logged to the standard error stream, and the execution of the application is aborted.
  In older versions, the profiler creates the coverage files at `C:\Users\Public`, and you can find the corresponding error message on the top of the coverage files.
* The target directory does not exist or is not writeable by the user who runs the executable.
  To solve this, check which user runs the process to be profiled and check the write permissions of the directory.
  :::

## Step 3: Uploading Coverage to Teamscale

The Teamscale .NET Profiler comes with the ability to upload coverage directly to a Teamscale instance.
During the upload, the profiler maps the recorded coverage to the source code of the application under test, using the respective PDB files.

:::tip Assumption: Reachability of Teamscale
This guide assumes that the Teamscale server is directly reachable from the test environment via HTTP(S).
Alternatively, the profiler can [upload reports to a file share][upload-to-fileshare], where they may be picked up and forwarded from another machine.
:::

### Step 3.1: Preparing Teamscale

In Teamscale, go to *Project Configuration* and click *New project*.

Name the new project `blogifier` and choose the analysis profile *C# (default)* for the default code scope in the *Code Scope Configuration* section.

In the *Branching Configuration*, change all start dates to `2019-09-01`.

In the *Connector Configuration* section, click `+` and choose *Source Code Repository* > *Git*.
Create a new account named `Blogifier GitHub` with the URI `https://github.com/blogifierdotnet/Blogifier.git` and without credentials.
Set the *Default branch name* to `main`.

Click on *Expert Options* and set the *End revision* to `2022-02-16`.
This day is after the `v3.0` release, so this way Teamscale will analyze all changes between `v2.5` and `v3.0`.

:::tip Start revision and Branching Configuration interact
If you create a project with more than one source code connector (e.g. to integrate multiple Git repositories into one Teamscale project), you can control how much history is analyzed by Teamscale:

* centrally via the *Branching Configuration*,
* separately for each connector via the *Start Revision*.

If you set both, the more recent of the two dates is applied to the connector.
:::

:::tip Default end revision
In this example we have set the *End revision* to shorten the analyzed history.
If you want to analyze your code continuously, you have to leave *End revision* set to the default value.
This way Teamscale will always analyze up to the latest revision of the repository.
:::

Click *Create project*.

![Create project "Blogifier" in Teamscale](./teamscale-create-project.png)

### Step 3.2: Linking the Version Under Test to a Source Code Revision

Create the file `C:\revision.txt` and set the file's content to `revision: 8e634ab4b93a60df0e216f3929b35c17cbffb1ca`.
This revision identifies the source code corresponding to the Blogifier 3.0 release, which we want to link the coverage information to.

:::tip Keep the Version Under Test and revision.txt in Sync
If you change the version under test, you also need to adjust the `revision.txt` accordingly.
We recommend to automate this in real test environments.
:::

:::tip Always Using Latest Revision
If you would like to upload the coverages to the last commit in your repository, you can provide `revision: HEAD` as a value in the `revision.txt` file.
:::

### Step 3.3: Uploading Coverage

Edit `C:\teamscale_dotnet_profiler\Profiler.yml` again and change the global configuration section to match the following:

```yaml{11,15-18,21,22,25}
{
  # ...
  match: [
    {
    # This section matches all processes.
    # We configure shared options here but disable the profiler.
    # The later match sections will enable the profiler for specific processes.
      profiler: {
        enabled: false,
        targetdir: "C:\\blogifier_coverage",
        upload_daemon: true
      },
      uploader: {
        teamscale: {
          url: "http://localhost:8080", # use your URL
          username: "admin", # use your username
          accessKey: "u7a9abc32r45r2uiig3vvv", # use your access key
          project: "blogifier",
          partition: "Manual Tests"
        },
        pdbDirectory: "C:\\path\\to\\Blogifier", # use the folder where Blogifier.pdb is located
        revisionFile: "C:\\revision.txt",
        assemblyPatterns: {
          include: [
            "Blogifier.*"
          ]
        }
      }
    },
    # These sections turn on the profiler for specific executables.
    {
      executableName: "Blogifier.exe",
      profiler: {
        enabled: true
      }
    }
  ]
}
```

This globally enables and configures automatic uploading of coverage data.

Use the URL and credentials (username and [access key](../../glossary/#access-key)) of your Teamscale instance.
The `project` option contains the ID of your Teamscale project such that the coverage data is sent to this project.
The `partition` is an arbitrary label that you may use to distinguish coverage source, such as different test stages.
Teamscale merges all coverage in the same partition and allows you to inspect coverage in different partitions individually (or in any combination).

During the upload, the Teamscale .NET Profiler maps the coverage data back to a particular version of the source code, using the PDB files and the revision ID from the `revision.txt`.
It will do so for all the assemblies of Blogifier (whose names match the regular expression `Blogifier.*`).

To test your setup, start `Blogifier.bat`, do something (e.g., create a new blog entry), and stop it again.
The profiler automatically schedules uploading of coverage data from terminated processes every five minutes.

:::tip Project state is "On hold"
If the uploaded data targets an old commit of the project, Teamscale does not schedule analyzing the data automatically, to prevent large [rollbacks](../../glossary/#rollback).
To proceed, navigate to the Project Configuration > Projects view.
When the data is uploaded, the state of the project changes to "On hold".
Click on the "On hold" label, then on the "Start Now" button.

![Project is On Hold after old coverage upload](./teamscale-on-hold.png)

![Postponed rollback can be started by clicking on the Start Now button](./teamscale-start-postponed-rollback.png)

:::

To verify that everything worked as expected, go to the *Activity* perspective.
If you filter your commits for the Commit Type "External Analysis", and the data was processed successfully, you will see the coverage upload.

![Coverage upload in the project activity](./teamscale-activity-coverage.png)

:::warning Environment variables
Note that if you did not set the environment variables system-wide, but inside a launch script, ensure that you are starting Blogifier using the launch script (i.e. `Blogifier.bat`) instead of running `Blogifier.exe` directly.
Otherwise this will result in a wrong configuration of the environment variables.
:::
:::warning Permissions
Ensure that the user running the executable has read access to the folder containing the PDB files.
Otherwise, processing the coverage will fail.
:::

:::tip Upload Interval
You may configure the upload interval in the `Profiler.yml` file, using the `uploadIntervalInMinutes` option.
:::

:::tip Reduce Performance Impact
Mapping coverage data to source code takes some processing time that adds to your overall test execution time.
To avoid wasting time on unnecessary mapping, ensure you always **configure the minimal set of assemblies**, i.e., exactly the assemblies containing the code that you want to identify test gaps in.
:::

:::tip Missing Uploads
If the Upload Daemon has failed to upload the coverage files, you can find the error logs at `C:\teamscale_dotnet_profiler\UploadDaemon\UploadDaemon.log`.
:::

## Step 4: Analyzing Coverage and Test Gaps

### Step 4.1: Test Gaps at File Level

In Teamscale, go to *Test Gaps > Files* and set the baseline to Git Tag `v2.5` at the top.

:::tip Test Gaps Between Two Dates
If you need to see the test gaps between two specific dates:

* first you have to select the start date as a baseline at the top of the view,
* then click on "Latest Test Gaps" to activate the time travel mode, and select the end date.

After closing the time travel dialog, the view will display the test gaps between the two selected dates.
:::

The table on the page shows the file-system structure of the Blogifier project, with *Test Gaps*, *Execution*, and *Churn* data aggregated for each file or directory.
You may drill down into the folders and click on the colored bar charts to check what exactly has been changed or executed and where the test gaps are.

![Test Gaps, Coverage, and Churn at File-System Level](./teamscale-tga-tests-perspective.png)

:::warning Hint
The Execution and Test Gap data may look different for you, depending on the actions you performed in Blogifier.
:::

### Step 4.2: Test Gaps at Method Level

In Teamscale, go to *Dashboard*.
In the top bar, click the `+` button and select *Add a new dashboard*. Then choose *Test Gap Treemap* from the list of widgets.
Finally, click *Save as* and name the Dashboard `Blogifier Test Gaps`.

Move the mouse over the Test Gap Treemap and click on the  icon that appears in the upper left, to edit the widget.
As the *Baseline*, select the Git tag `v2.5`.
Make sure that *Manual Tests* is selected under *Coverage Sources*, for the widget to consider the coverage you uploaded earlier.
Confirm the dialog.

![Configuring a Test-Gap Treemap on a Teamscale Dashboard](./teamscale-tga-widget-settings.png)

The widget now shows the code changes between version 2.5 and 3.0 in color.
The red and yellow blocks represent methods that have been changed, but were not covered by tests, i.e., test gaps.
Hover the mouse over the blocks in the treemap to see details in a tooltip.
Click on the blocks to drill into the data.

![Test Gaps on the Code Changes between Version 2.5 and 3.0 of Blogifier](./teamscale-tga-widget.png)

:::warning Hint
The treemap may look different for you, depending on the actions you performed in Blogifier.
:::

***

#### Further Reading:

1. [Test Gap Analysis](../../reference/test-gap-analysis/)
2. [Test Gaps Perspective](../../reference/ui/testgaps/)
3. [How to Create a Test Gap Analysis Dashboard](../../howto/creating-a-tga-dashboard/)
4. [Working with Test Gap Treemaps](../../tutorial/working-with-test-gap-treemaps/)

[dotnet]: https://github.com/cqse/teamscale-profiler-dotnet/

[dotnet-releases]: https://github.com/cqse/teamscale-profiler-dotnet/releases

[blogifier]: https://github.com/blogifierdotnet/Blogifier

[blogifier-3.0-release]: https://github.com/blogifierdotnet/Blogifier/releases/tag/v3.0

[upload-to-fileshare]: ../../reference/coverage-profilers/teamscale-dotnet-profiler/config-reference/#network-share
