Skip to content
Merged
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
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,93 @@ public async Task HasFix(string testCase)
}
```

### Fixture configuration

Every `RoslynFixtureFactory.Create<T>()` overload accepts an optional config object. The config classes share a common base (`BaseTestFixtureConfig`) that exposes project-level settings. Fixture-specific config classes inherit from this base and can add extra options on top.

#### Shared options (all fixture types)

| Property | Type | Default | Description |
|---|---|---|---|
| `Language` | `string` | `LanguageNames.AL` | Language used for the test project. |
| `ThrowsWhenInputDocumentContainsError` | `bool` | `true` | Throw when the code under test has compiler errors. |
| `References` | `IReadOnlyList<MetadataReference>` | empty | Extra metadata references added to the project. |
| `AdditionalFiles` | `IReadOnlyList<AdditionalText>` | empty | Additional files exposed to analyzers (e.g. `.editorconfig`). |
| `RuleSetPath` | `string?` | `null` | Path to a `.ruleset` file that controls diagnostic severity. |
| `PackageCachePaths` | `IReadOnlyList<string>` | empty | Directories containing `.app` packages the compiler resolves symbols from. |
| `CompilationOptions` | `CompilationOptions?` | `null` | Override the default `CompilationOptions`. |
| `ParseOptions` | `ParseOptions?` | `null` | Override the default `ParseOptions`. |
| `ProjectInfoCustomizer` | `Func<ProjectInfo, ProjectInfo>?` | `null` | Escape hatch to set any `ProjectInfo` property not exposed above. |

#### CodeFixTestFixtureConfig extras

| Property | Type | Default | Description |
|---|---|---|---|
| `AdditionalAnalyzers` | `IReadOnlyCollection<DiagnosticAnalyzer>` | empty | Analyzers that produce the diagnostics the code fix needs to act on. |

#### Example: apply a ruleset to an analyzer test

```csharp
var fixture = RoslynFixtureFactory.Create<FlowFieldsShouldNotBeEditable>(
new AnalyzerTestFixtureConfig
{
RuleSetPath = @"rules\my.ruleset"
});

fixture.HasDiagnosticAtAllMarkers(code, DiagnosticIds.FlowFieldsShouldNotBeEditable);
```

#### Example: resolve symbols from a package cache

```csharp
var fixture = RoslynFixtureFactory.Create<FlowFieldsShouldNotBeEditable>(
new AnalyzerTestFixtureConfig
{
PackageCachePaths = [@"C:\packages\base-app"]
});

fixture.HasDiagnosticAtAllMarkers(code, DiagnosticIds.FlowFieldsShouldNotBeEditable);
```

#### Example: set compilation target to OnPrem

```csharp
var fixture = RoslynFixtureFactory.Create<FlowFieldsShouldNotBeEditable>(
new AnalyzerTestFixtureConfig
{
CompilationOptions = new CompilationOptions(target: CompilationTarget.OnPrem)
});

fixture.HasDiagnosticAtAllMarkers(code, DiagnosticIds.FlowFieldsShouldNotBeEditable);
```

#### Example: code fix with additional analyzers and a ruleset

```csharp
var fixture = RoslynFixtureFactory.Create<FlowFieldsShouldNotBeEditableCodeFixProvider>(
new CodeFixTestFixtureConfig
{
AdditionalAnalyzers = [new Analyzer.FlowFieldsShouldNotBeEditable()],
RuleSetPath = @"rules\my.ruleset"
});

fixture.TestCodeFix(currentCode, expectedCode, DiagnosticDescriptors.FlowFieldsShouldNotBeEditable);
```

#### Example: escape hatch for advanced ProjectInfo settings

When you need to configure a `ProjectInfo` property that has no dedicated config option, use `ProjectInfoCustomizer`.

```csharp
var fixture = RoslynFixtureFactory.Create<FlowFieldsShouldNotBeEditable>(
new AnalyzerTestFixtureConfig
{
ProjectInfoCustomizer = info => info.WithAssemblyProbingPaths([@"C:\probing"])
});

fixture.HasDiagnosticAtAllMarkers(code, DiagnosticIds.FlowFieldsShouldNotBeEditable);
```

### NavCodeAnalysisBase
The `NavCodeAnalysisBase` class is a base class for analyzer tests that need to behave differently depending on the version of the AL Language (`Microsoft.Dynamics.Nav.CodeAnalysis`). By inheriting from this class, your tests automatically gain utilities for:
* Detecting the currently loaded Microsoft.Dynamics.Nav.CodeAnalysis assembly version.
Expand Down
44 changes: 42 additions & 2 deletions src/RoslynTestKit/BaseTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using Document = Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces.Document;
using LanguageNames = Microsoft.Dynamics.Nav.CodeAnalysis.LanguageNames;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -20,6 +22,36 @@

protected virtual IReadOnlyCollection<AdditionalText>? AdditionalFiles => null;

/// <summary>
/// Path to a ruleset file (.ruleset) applied to the test project.
/// When null, the default ruleset behaviour applies.
/// </summary>
protected virtual string? RuleSetPath => null;

/// <summary>
/// Package cache paths made available to the compiler when resolving symbols.
/// </summary>
protected virtual IReadOnlyList<string>? PackageCachePaths => null;

/// <summary>
/// Override the <see cref="CompilationOptions"/> used when compiling the test project.
/// When null, <c>new CompilationOptions()</c> is used.
/// </summary>
protected virtual CompilationOptions? CustomCompilationOptions => null;

/// <summary>
/// Override the <see cref="ParseOptions"/> applied to the test project.
/// When null, the platform default applies.
/// </summary>
protected virtual ParseOptions? ParseOptions => null;

/// <summary>
/// Optional callback applied to the <see cref="ProjectInfo"/> after all other settings have
/// been applied. Use this escape hatch to configure any <see cref="ProjectInfo"/> property
/// not directly exposed on the fixture.
/// </summary>
protected virtual Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer => null;

protected Document CreateDocumentFromCode(string code)
{
return CreateDocumentFromCode(code, LanguageName, References ?? Array.Empty<MetadataReference>());
Expand All @@ -35,7 +67,15 @@
{
var frameworkReferences = CreateFrameworkMetadataReferences();

var compilationOptions = GetCompilationOptions(languageName);
var compilationOptions = CustomCompilationOptions ?? GetCompilationOptions(languageName);

var settings = new ProjectSettings
{
RuleSetPath = RuleSetPath,
PackageCachePaths = PackageCachePaths,
ParseOptions = ParseOptions,
ProjectInfoCustomizer = ProjectInfoCustomizer
};

var docs = FileSeparatorPattern.Split(code).Reverse().ToList();
if (docs.Count == 0)
Expand All @@ -43,8 +83,8 @@
throw new ArgumentException("Code cannot be empty after splitting", nameof(code));
}

var project = new AdhocWorkspace()

Check warning on line 86 in src/RoslynTestKit/BaseTestFixture.cs

View workflow job for this annotation

GitHub Actions / Build, Pack and Validate

Dereference of a possibly null reference.

Check warning on line 86 in src/RoslynTestKit/BaseTestFixture.cs

View workflow job for this annotation

GitHub Actions / Build, Pack and Validate

Dereference of a possibly null reference.
.AddProject("TestProject", languageName)
.AddProject("TestProject", languageName, settings)
.WithCompilationOptions(compilationOptions)
.AddMetadataReferences(frameworkReferences)
.AddMetadataReferences(extraReferences);
Expand Down
36 changes: 35 additions & 1 deletion src/RoslynTestKit/BaseTestFixtureConfig.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using LanguageNames = Microsoft.Dynamics.Nav.CodeAnalysis.LanguageNames;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -14,5 +18,35 @@ public abstract class BaseTestFixtureConfig
public string Language { get; set; } = LanguageNames.AL;

public IReadOnlyList<AdditionalText> AdditionalFiles { get; set; } = ImmutableArray<AdditionalText>.Empty;

/// <summary>
/// Path to a ruleset file (.ruleset) that controls which diagnostics are enabled and at what severity.
/// When null, the default ruleset behaviour applies.
/// </summary>
public string? RuleSetPath { get; set; } = null;

/// <summary>
/// Paths to directories that contain .app packages the compiler should resolve symbols from.
/// </summary>
public IReadOnlyList<string> PackageCachePaths { get; set; } = ImmutableArray<string>.Empty;

/// <summary>
/// Override the default <see cref="CompilationOptions"/> used when compiling the test project.
/// When null, <c>new CompilationOptions()</c> is used.
/// </summary>
public CompilationOptions? CompilationOptions { get; set; } = null;

/// <summary>
/// Override the default <see cref="ParseOptions"/> used when parsing test documents.
/// When null, the platform default applies.
/// </summary>
public ParseOptions? ParseOptions { get; set; } = null;

/// <summary>
/// Optional callback applied to the <see cref="ProjectInfo"/> after all other settings have been
/// applied. Use this escape hatch to set any <see cref="ProjectInfo"/> property not exposed
/// directly on the config.
/// </summary>
public Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer { get; set; } = null;
}
}
11 changes: 10 additions & 1 deletion src/RoslynTestKit/ConfigurableAnalyzerTestFixture.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -21,5 +25,10 @@ public ConfigurableAnalyzerTestFixture(DiagnosticAnalyzer diagnosticAnalyzer, An
protected override IReadOnlyCollection<MetadataReference> References => _config.References;
protected override IReadOnlyCollection<AdditionalText> AdditionalFiles => _config.AdditionalFiles;
protected override bool ThrowsWhenInputDocumentContainsError => _config.ThrowsWhenInputDocumentContainsError;
protected override string? RuleSetPath => _config.RuleSetPath;
protected override IReadOnlyList<string>? PackageCachePaths => _config.PackageCachePaths;
protected override CompilationOptions? CustomCompilationOptions => _config.CompilationOptions;
protected override ParseOptions? ParseOptions => _config.ParseOptions;
protected override Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer => _config.ProjectInfoCustomizer;
}
}
11 changes: 10 additions & 1 deletion src/RoslynTestKit/ConfigurableCodeFixTestFixture.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.CodeFixes;
using Microsoft.Dynamics.Nav.CodeAnalysis.Diagnostics;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -23,5 +27,10 @@ public ConfigurableCodeFixTestFixture(CodeFixProvider provider, CodeFixTestFixtu
protected override IReadOnlyCollection<MetadataReference> References => _config.References;
protected override IReadOnlyCollection<AdditionalText> AdditionalFiles => _config.AdditionalFiles;
protected override bool ThrowsWhenInputDocumentContainsError => _config.ThrowsWhenInputDocumentContainsError;
protected override string? RuleSetPath => _config.RuleSetPath;
protected override IReadOnlyList<string>? PackageCachePaths => _config.PackageCachePaths;
protected override CompilationOptions? CustomCompilationOptions => _config.CompilationOptions;
protected override ParseOptions? ParseOptions => _config.ParseOptions;
protected override Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer => _config.ProjectInfoCustomizer;
}
}
11 changes: 10 additions & 1 deletion src/RoslynTestKit/ConfigurableCodeRefactoringTestFixture.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.CodeRefactoring;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -21,5 +25,10 @@ public ConfigurableCodeRefactoringTestFixture(CodeRefactoringProvider provider,
protected override IReadOnlyCollection<MetadataReference> References => _config.References;
protected override IReadOnlyCollection<AdditionalText> AdditionalFiles => _config.AdditionalFiles;
protected override bool ThrowsWhenInputDocumentContainsError => _config.ThrowsWhenInputDocumentContainsError;
protected override string? RuleSetPath => _config.RuleSetPath;
protected override IReadOnlyList<string>? PackageCachePaths => _config.PackageCachePaths;
protected override CompilationOptions? CustomCompilationOptions => _config.CompilationOptions;
protected override ParseOptions? ParseOptions => _config.ParseOptions;
protected override Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer => _config.ProjectInfoCustomizer;
}
}
11 changes: 10 additions & 1 deletion src/RoslynTestKit/ConfigurableCompletionProviderTestFixture.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces;
using Microsoft.Dynamics.Nav.CodeAnalysis.Workspaces.Completion;
using AdditionalText = Microsoft.Dynamics.Nav.CodeAnalysis.AdditionalText;
using CompilationOptions = Microsoft.Dynamics.Nav.CodeAnalysis.CompilationOptions;
using ParseOptions = Microsoft.Dynamics.Nav.CodeAnalysis.ParseOptions;

namespace RoslynTestKit
{
Expand All @@ -21,5 +25,10 @@ public ConfigurableCompletionProviderTestFixture(CompletionProvider provider, Co
protected override IReadOnlyCollection<MetadataReference> References => _config.References;
protected override IReadOnlyCollection<AdditionalText> AdditionalFiles => _config.AdditionalFiles;
protected override bool ThrowsWhenInputDocumentContainsError => _config.ThrowsWhenInputDocumentContainsError;
protected override string? RuleSetPath => _config.RuleSetPath;
protected override IReadOnlyList<string>? PackageCachePaths => _config.PackageCachePaths;
protected override CompilationOptions? CustomCompilationOptions => _config.CompilationOptions;
protected override ParseOptions? ParseOptions => _config.ParseOptions;
protected override Func<ProjectInfo, ProjectInfo>? ProjectInfoCustomizer => _config.ProjectInfoCustomizer;
}
}
Loading
Loading