Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4a5750d
test: Add comprehensive tests for DiagnosticEditProperties methods
rjmurillo Jul 26, 2025
55867eb
test: Add unit tests for DiagnosticExtensions methods
rjmurillo Jul 26, 2025
df1886c
test: Add unit test for CreateDiagnostic_FromOperation_WithProperties…
rjmurillo Jul 26, 2025
e51072f
test: Add unit tests for MoqVerificationHelpers methods
rjmurillo Jul 26, 2025
da87546
test: Add unit test for NoFixForExplicitInterfaceImplementation method
rjmurillo Jul 27, 2025
ca2f3bc
docs: Update C# file instructions to enforce valid code in tests
rjmurillo Jul 27, 2025
0bc2ba6
docs: Update C# file instructions to include code coverage validation…
rjmurillo Jul 27, 2025
63d39b6
test: Update test data for RedundantTimesSpecificationAnalyzer to inc…
rjmurillo Jul 27, 2025
774d2b2
docs: Update CONTRIBUTING.md to include code coverage validation requ…
rjmurillo Jul 27, 2025
a89a5d2
refactor: Remove unused using directives in MoqVerificationHelpersTests
rjmurillo Jul 27, 2025
368dffc
test: Remove redundant entry from test data in RedundantTimesSpecific…
rjmurillo Jul 27, 2025
2e85b52
test: Add additional cases for ExtractLambdaFromArgument in MoqVerifi…
rjmurillo Jul 27, 2025
59bf028
test: Enhance InvalidTestData with detailed comments and add verifica…
rjmurillo Jul 27, 2025
2067433
test: Add edge case tests for LinqToMocks expressions and refine exis…
rjmurillo Jul 27, 2025
bc5e21f
refactor: Simplify TestData method by using array initializers and im…
rjmurillo Jul 27, 2025
50267de
test: Enhance TestData method with additional edge cases and improve …
rjmurillo Jul 27, 2025
30c9fce
test: Remove unnecessary test case for property access in TestData me…
rjmurillo Jul 27, 2025
7a2df5c
test: Update CodeCoverage settings to exclude additional attributes a…
rjmurillo Jul 27, 2025
af00375
test: Add coverage history management to CI workflow and update cover…
rjmurillo Jul 27, 2025
e928265
test: Add baseline SHA to performance results summary
rjmurillo Jul 27, 2025
48662fd
test: Add comprehensive tests for DiagnosticEditProperties methods
rjmurillo Jul 26, 2025
26347b2
test: Add unit tests for DiagnosticExtensions methods
rjmurillo Jul 26, 2025
c284cc5
test: Add unit test for CreateDiagnostic_FromOperation_WithProperties…
rjmurillo Jul 26, 2025
351b6c3
test: Add unit tests for MoqVerificationHelpers methods
rjmurillo Jul 26, 2025
a260489
test: Add unit test for NoFixForExplicitInterfaceImplementation method
rjmurillo Jul 27, 2025
3114a71
docs: Update C# file instructions to enforce valid code in tests
rjmurillo Jul 27, 2025
656e0b4
docs: Update C# file instructions to include code coverage validation…
rjmurillo Jul 27, 2025
ca0aa8b
test: Update test data for RedundantTimesSpecificationAnalyzer to inc…
rjmurillo Jul 27, 2025
afebfd9
docs: Update CONTRIBUTING.md to include code coverage validation requ…
rjmurillo Jul 27, 2025
50024c5
refactor: Remove unused using directives in MoqVerificationHelpersTests
rjmurillo Jul 27, 2025
b7fb7a9
test: Remove redundant entry from test data in RedundantTimesSpecific…
rjmurillo Jul 27, 2025
ea9b2f5
test: Add additional cases for ExtractLambdaFromArgument in MoqVerifi…
rjmurillo Jul 27, 2025
b474f53
test: Enhance InvalidTestData with detailed comments and add verifica…
rjmurillo Jul 27, 2025
43024eb
test: Add edge case tests for LinqToMocks expressions and refine exis…
rjmurillo Jul 27, 2025
8059df2
refactor: Simplify TestData method by using array initializers and im…
rjmurillo Jul 27, 2025
87937b2
test: Enhance TestData method with additional edge cases and improve …
rjmurillo Jul 27, 2025
8f321e0
test: Remove unnecessary test case for property access in TestData me…
rjmurillo Jul 27, 2025
93bc5a2
test: Update CodeCoverage settings to exclude additional attributes a…
rjmurillo Jul 27, 2025
3a543bc
test: Add coverage history management to CI workflow and update cover…
rjmurillo Jul 27, 2025
39c3999
test: Add baseline SHA to performance results summary
rjmurillo Jul 27, 2025
cabb69e
Merge branch 'refactor/coverage-analysis' of https://github.com/rjmur…
rjmurillo Jul 27, 2025
00049eb
test: Remove upload step for coverage history in CI workflow
rjmurillo Jul 27, 2025
ada8199
fix: Update condition for uploading coverage history in CI workflow
rjmurillo Jul 27, 2025
5632bf2
test: Enhance edge case test data for LinqToMocksExpressionShouldBeVa…
rjmurillo Jul 27, 2025
9dccdc1
refactor: Remove redundant comment in LinqToMocksExpressionShouldBeVa…
rjmurillo Jul 27, 2025
930c720
refactor: Remove TryGetFromImmutableDictionary_ParsesCultureInvariant…
rjmurillo Jul 27, 2025
072a688
refactor: Remove unused using directives from analyzer and test files
rjmurillo Jul 27, 2025
56775f4
Merge branch 'main' into refactor/coverage-analysis
rjmurillo Jul 27, 2025
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
22 changes: 19 additions & 3 deletions .github/instructions/csharp.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@

- **For complex changes, see the Decision Trees section below**

**MANDATORY: Only Valid C# Code in All Tests**

Check notice on line 11 in .github/instructions/csharp.instructions.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

.github/instructions/csharp.instructions.md#L11

Emphasis used instead of a heading

> **You MUST NEVER write or include code in analyzer or code fix tests that produces C# compiler errors.**
> - All test code must be valid, compilable C#.

Check notice on line 14 in .github/instructions/csharp.instructions.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

.github/instructions/csharp.instructions.md#L14

Lists should be surrounded by blank lines
> - Do not write tests for static, const, readonly, or event members if the code would not compile.
> - Do not include code that triggers CSxxxx errors (e.g., invalid member access, missing members, or illegal syntax).
> - If a scenario cannot be expressed as valid C#, it is not a valid test case for analyzers or code fixes.
> - Any test that fails to compile is an immediate failure and must be removed or rewritten.

**Rationale:**
- Roslyn analyzers and code fixes only operate on valid, successfully parsed C# code. Compiler errors prevent analyzers from running and invalidate the test scenario.

Check notice on line 21 in .github/instructions/csharp.instructions.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

.github/instructions/csharp.instructions.md#L21

Lists should be surrounded by blank lines
- Writing invalid code in tests causes build failures, test failures, and wastes review/CI resources.
- This is a non-negotiable rule. If you are unsure whether a test is valid C#, STOP and request expert guidance.

Comment thread
rjmurillo marked this conversation as resolved.
## Primary Instructions

Always read and apply the instructions in [.github/copilot-instructions.md](../copilot-instructions.md) when working on C# source or project files.
Expand Down Expand Up @@ -68,9 +82,10 @@
- Update AnalyzerReleases.Unshipped.md
5. **Update project files if needed**
6. **Run all validations (build, test, lint, Codacy, etc.)**
7. **Prepare PR with validation evidence for each file type**
8. **If any diagnostic span or test fails more than once, STOP and escalate**
9. **If uncertain about Roslyn APIs, Moq semantics, or workflow, escalate**
7. **Validate code coverage of your changed code**
8. **Prepare PR with validation evidence for each file type**
9. **If any diagnostic span or test fails more than once, STOP and escalate**
10. **If uncertain about Roslyn APIs, Moq semantics, or workflow, escalate**

## Test Data & Sample Inputs/Outputs

Expand All @@ -87,3 +102,4 @@
- Data-driven tests for all fixable patterns
- Performance test if analyzer is non-trivial
- Document test data rationale in comments or PR description
- Code coverage is automatically generated when `dotnet test --settings ./build/targets/tests/test.runsettings` is run and placed in `./artifacts/TestResults/coverage/Cobertura.xml`
24 changes: 24 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
env:
IS_CODACY_COVERAGE_ALLOWED: ${{ secrets.CODACY_PROJECT_TOKEN != '' }}
IS_QLTY_COVERAGE_ALLOWED: ${{ secrets.QLTY_COVERAGE_TOKEN != '' }}
IS_TARGET_MAIN: ${{ github.ref == 'refs/heads/main' }}
RUN_FULL_PERF: ${{ (github.event_name == 'schedule' && github.ref == 'refs/heads/main') || (github.event_name == 'workflow_dispatch' && github.event.inputs.run_performance == 'true') }}
FORCE_PERF_BASELINE: ${{ github.event.inputs.force_baseline }} # This is also used in PerfCore.ps1 to determine if the baseline should be forced

Expand All @@ -55,11 +56,25 @@ jobs:
- name: Setup, Restore, and Build Solution
uses: ./.github/actions/setup-restore-build

- name: Restore Code Coverage history
uses: actions/download-artifact@v4
with:
name: CoverageHistory-${{ matrix.os }}
path: ./artifacts/TestResults/coveragehistory
continue-on-error: true

Comment thread
rjmurillo marked this conversation as resolved.
- name: Test
run: dotnet test --no-build --configuration Release --settings ./build/targets/tests/test.runsettings
env:
REPORTGENERATOR_LICENSE: ${{ secrets.REPORTGENERATOR_LICENSE }}

- name: Upload coverage history
uses: actions/upload-artifact@v4
if: success() && env.IS_TARGET_MAIN == 'true'
with:
name: CoverageHistory-${{ matrix.os }}
path: ./artifacts/TestResults/coveragehistory

- name: Upload binlogs
uses: actions/upload-artifact@v4
if: success() || failure()
Expand Down Expand Up @@ -173,6 +188,7 @@ jobs:
$resultsDir = "artifacts/performance/perfResults/baseline/results"
if (Test-Path $resultsDir) {
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Baseline Performance Results"
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "Baseline SHA: ${{ steps.get-baseline-sha.outputs.sha }}"
$files = Get-ChildItem -Path $resultsDir -Filter "*-report-github.md" | Sort-Object Name
foreach ($file in $files) {
Get-Content $file.FullName | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY
Expand Down Expand Up @@ -201,3 +217,11 @@ jobs:
name: performance-${{ matrix.os }}
path: |
./artifacts/performance/**

- name: Upload artifacts
uses: actions/upload-artifact@v4
if: success() || failure()
with:
name: artifacts-${{ matrix.os }}
path: ./artifacts
if-no-files-found: error
Comment thread
rjmurillo marked this conversation as resolved.
11 changes: 9 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,18 @@ ci(workflow): add performance testing to nightly builds
Run all unit tests:
`dotnet test --settings ./build/targets/tests/test.runsettings`
All tests must pass. PRs with failing tests will be closed.
- **Code Coverage:**
Code coverage is generated automatically if you use the `test.runsettings` from above, which produces Cobertura format. If you wish to have a different format, you can specify on the command line. Example:
`dotnet test --collect:"XPlat Code Coverage" --settings ./build/targets/tests/test.runsettings`
PRs must not reduce coverage for critical paths without justification.
- **Codacy Analysis:**
Run Codacy CLI analysis on all changed files. Fix all reported issues before submitting the PR.
- **Evidence Required:**
PR description must include console output or screenshots for:
- `dotnet format`
- `dotnet build`
- `dotnet test`
- **Code coverage report summary**
- Codacy analysis (if issues were found and fixed)
- **No Received Files:**
Remove any `*.received.*` files before committing.
Expand Down Expand Up @@ -665,16 +670,18 @@ Before submitting a PR, ensure:

**What Constitutes Validation Evidence:**
- Test execution logs showing all tests pass
- **Code coverage report summary and/or screenshots showing coverage for changed code**
- Performance benchmark results (if applicable)
- Screenshots of successful CI runs
- Manual testing results for UI changes
- Code coverage reports for new features

**Evidence Format:**
- Include logs, screenshots, or links to CI runs
- Provide clear, readable evidence
- Ensure evidence is recent and relevant
- Link to specific test results or benchmarks
- Link to specific test results, code coverage reports, or benchmarks

**MANDATORY:** Always validate code coverage after making code or test changes. Code coverage validation is a required step before yielding, submitting, or completing any task. Coverage must be reported as part of the validation evidence in PRs and reviews.

### Evidence of Moq Version Awareness

Expand Down
4 changes: 3 additions & 1 deletion build/targets/tests/Tests.targets
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<_TestCoverageGlob>$(ArtifactsTestResultsPath)/**/*.cobertura.xml</_TestCoverageGlob>
<_TestCoverageReportDirectory>$(ArtifactsTestResultsPath)/coverage</_TestCoverageReportDirectory>
<_TestCoverageHistoryDirectory>$(ArtifactsTestResultsPath)/coveragehistory</_TestCoverageHistoryDirectory>
</PropertyGroup>

<Target Name="CleanCoverageReport" BeforeTargets="Test;VSTest" Condition=" '$(IsTestProject)' == 'true' ">
Expand All @@ -30,6 +31,7 @@
ProjectDirectory="$(MSBuildProjectDirectory)"
ReportFiles="@(_CoverageFiles)"
TargetDirectory="$(_TestCoverageReportDirectory)"
ReportTypes="MarkdownSummaryGithub;Cobertura;HtmlInline" />
ReportTypes="MarkdownSummaryGithub;Cobertura;HtmlInline"
HistoryDirectory="$(_TestCoverageHistoryDirectory)" />
</Target>
</Project>
4 changes: 4 additions & 0 deletions build/targets/tests/test.runsettings
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
<CodeCoverage>
<EnableStaticNativeInstrumentation>False</EnableStaticNativeInstrumentation>
<EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation>
<ExcludeByAttribute>Obsolete,GeneratedCodeAttribute,CompilerGeneratedAttribute</ExcludeByAttribute>
<SkipAutoProps>true</SkipAutoProps>
<ModulePaths>
<Include>
<!--
Expand Down Expand Up @@ -45,6 +47,8 @@
<Function>^get_.*</Function> <!-- Excludes property getters -->
<Function>^set_.*</Function> <!-- Excludes property setters -->
<Function>^.*Test.*</Function> <!-- Excludes test methods -->
<Function>^Microsoft\.Testing.*</Function>
<Function>^System\.Diagnostics.*</Function>
</Exclude>
</Functions>
</CodeCoverage>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.CodeAnalysis.Operations;
using Moq.Analyzers.Common;

namespace Moq.Analyzers;

Expand Down
2 changes: 0 additions & 2 deletions src/Analyzers/MockRepositoryVerifyAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;

namespace Moq.Analyzers;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;

namespace Moq.Analyzers;
Expand Down
3 changes: 0 additions & 3 deletions src/Common/EventSyntaxExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Moq.Analyzers.Common;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,61 @@ public void ToImmutableDictionary_RoundTripsProperties()
Assert.Equal("2", dict[DiagnosticEditProperties.EditPositionKey]);
}

[Fact]
public void ToImmutableDictionary_AllEnumValues_RoundTrips()
{
foreach (DiagnosticEditProperties.EditType type in Enum.GetValues(typeof(DiagnosticEditProperties.EditType)))
{
DiagnosticEditProperties props = new DiagnosticEditProperties { TypeOfEdit = type, EditPosition = 0 };
ImmutableDictionary<string, string?> dict = props.ToImmutableDictionary();
Assert.Equal(type.ToString(), dict[DiagnosticEditProperties.EditTypeKey]);
Assert.Equal("0", dict[DiagnosticEditProperties.EditPositionKey]);
Assert.True(DiagnosticEditProperties.TryGetFromImmutableDictionary(dict, out DiagnosticEditProperties? roundTripped));
Assert.Equal(type, roundTripped!.TypeOfEdit);
}
}

[Fact]
public void ToImmutableDictionary_HandlesNegativeAndLargeEditPosition()
{
DiagnosticEditProperties propsNeg = new DiagnosticEditProperties { TypeOfEdit = DiagnosticEditProperties.EditType.Insert, EditPosition = -1 };
ImmutableDictionary<string, string?> dictNeg = propsNeg.ToImmutableDictionary();
Assert.Equal("-1", dictNeg[DiagnosticEditProperties.EditPositionKey]);
Assert.True(DiagnosticEditProperties.TryGetFromImmutableDictionary(dictNeg, out DiagnosticEditProperties? roundTrippedNeg));
Assert.Equal(-1, roundTrippedNeg!.EditPosition);

DiagnosticEditProperties propsLarge = new DiagnosticEditProperties { TypeOfEdit = DiagnosticEditProperties.EditType.Replace, EditPosition = int.MaxValue };
ImmutableDictionary<string, string?> dictLarge = propsLarge.ToImmutableDictionary();
Assert.Equal(int.MaxValue.ToString(), dictLarge[DiagnosticEditProperties.EditPositionKey]);
Assert.True(DiagnosticEditProperties.TryGetFromImmutableDictionary(dictLarge, out DiagnosticEditProperties? roundTrippedLarge));
Assert.Equal(int.MaxValue, roundTrippedLarge!.EditPosition);
}

[Fact]
public void TryGetFromImmutableDictionary_IgnoresExtraKeys()
{
ImmutableDictionary<string, string?> dict = ImmutableDictionary<string, string?>.Empty
.Add(DiagnosticEditProperties.EditTypeKey, "Insert")
.Add(DiagnosticEditProperties.EditPositionKey, "1")
.Add("ExtraKey", "ExtraValue");
Assert.True(DiagnosticEditProperties.TryGetFromImmutableDictionary(dict, out DiagnosticEditProperties? props));
Assert.Equal(DiagnosticEditProperties.EditType.Insert, props!.TypeOfEdit);
Assert.Equal(1, props.EditPosition);
}

[Fact]
public void DiagnosticEditProperties_EqualityAndImmutability()
{
DiagnosticEditProperties a = new DiagnosticEditProperties { TypeOfEdit = DiagnosticEditProperties.EditType.Insert, EditPosition = 1 };
DiagnosticEditProperties b = new DiagnosticEditProperties { TypeOfEdit = DiagnosticEditProperties.EditType.Insert, EditPosition = 1 };
DiagnosticEditProperties c = new DiagnosticEditProperties { TypeOfEdit = DiagnosticEditProperties.EditType.Replace, EditPosition = 1 };
Assert.Equal(a, b);
Assert.NotEqual(a, c);
Assert.True(a.Equals(b));
Assert.False(a.Equals(c));
Assert.Equal(a.GetHashCode(), b.GetHashCode());
}

[Fact]
public void TryGetFromImmutableDictionary_Succeeds_WithValidDictionary()
{
Expand Down
58 changes: 58 additions & 0 deletions tests/Moq.Analyzers.Test/Common/DiagnosticExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
namespace Moq.Analyzers.Test.Common;

Check warning on line 1 in tests/Moq.Analyzers.Test/Common/DiagnosticExtensionsTests.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/Moq.Analyzers.Test/Common/DiagnosticExtensionsTests.cs#L1

Provide an 'AssemblyVersion' attribute for assembly 'srcassembly.dll'.

public class DiagnosticExtensionsTests
{
[Fact]
public void CreateDiagnostic_FromSyntaxNode_Basic()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText("class C { void M() {} }");
SyntaxNode root = tree.GetRoot();
DiagnosticDescriptor rule = new DiagnosticDescriptor("TEST0001", "Test", "Test message", "Test", DiagnosticSeverity.Warning, true);
Diagnostic diag = root.CreateDiagnostic(rule);
Assert.Equal("TEST0001", diag.Id);
Assert.Equal(DiagnosticSeverity.Warning, diag.Severity);
}

[Fact]
public void CreateDiagnostic_FromLocation_WithProperties()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText("class C { void M() {} }");
Location loc = tree.GetRoot().GetLocation();
DiagnosticDescriptor rule = new DiagnosticDescriptor("TEST0002", "Test2", "Test message 2", "Test", DiagnosticSeverity.Info, true);
ImmutableDictionary<string, string?> properties = ImmutableDictionary<string, string?>.Empty.Add("Key", "Value");
Diagnostic diag = loc.CreateDiagnostic(rule, properties);
Assert.Equal("TEST0002", diag.Id);
Assert.Equal("Value", diag.Properties["Key"]);
}

[Fact]
public void CreateDiagnostic_FromOperation_DelegatesToSyntax()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText("class C { void M() {} }");
SyntaxNode root = tree.GetRoot();
CSharpCompilation compilation = CSharpCompilation.Create("Test", new[] { tree });
SemanticModel model = compilation.GetSemanticModel(tree);
MethodDeclarationSyntax methodDecl = root.DescendantNodes().OfType<MethodDeclarationSyntax>().First();
Comment thread
rjmurillo marked this conversation as resolved.
Microsoft.CodeAnalysis.IOperation? operation = model.GetOperation(methodDecl);
DiagnosticDescriptor rule = new DiagnosticDescriptor("TEST0003", "Test3", "Test message 3", "Test", DiagnosticSeverity.Error, true);
Diagnostic diag = operation!.CreateDiagnostic(rule);
Assert.Equal("TEST0003", diag.Id);
Assert.Equal(DiagnosticSeverity.Error, diag.Severity);
}

[Fact]
public void CreateDiagnostic_FromOperation_WithProperties()
{
SyntaxTree tree = CSharpSyntaxTree.ParseText("class C { void M() {} }");
SyntaxNode root = tree.GetRoot();
CSharpCompilation compilation = CSharpCompilation.Create("Test", new[] { tree });
SemanticModel model = compilation.GetSemanticModel(tree);
MethodDeclarationSyntax methodDecl = root.DescendantNodes().OfType<MethodDeclarationSyntax>().First();
Microsoft.CodeAnalysis.IOperation? operation = model.GetOperation(methodDecl);
DiagnosticDescriptor rule = new DiagnosticDescriptor("TEST0004", "Test4", "Test message 4", "Test", DiagnosticSeverity.Warning, true);
ImmutableDictionary<string, string?> properties = ImmutableDictionary<string, string?>.Empty.Add("Key2", "Value2");
Diagnostic diag = operation!.CreateDiagnostic(rule, properties);
Assert.Equal("TEST0004", diag.Id);
Assert.Equal("Value2", diag.Properties["Key2"]);
}
}
Loading
Loading