ANcpLua.Roslyn.Utilities.Testing
1.56.1
dotnet add package ANcpLua.Roslyn.Utilities.Testing --version 1.56.1
NuGet\Install-Package ANcpLua.Roslyn.Utilities.Testing -Version 1.56.1
<PackageReference Include="ANcpLua.Roslyn.Utilities.Testing" Version="1.56.1" />
<PackageVersion Include="ANcpLua.Roslyn.Utilities.Testing" Version="1.56.1" />
<PackageReference Include="ANcpLua.Roslyn.Utilities.Testing" />
paket add ANcpLua.Roslyn.Utilities.Testing --version 1.56.1
#r "nuget: ANcpLua.Roslyn.Utilities.Testing, 1.56.1"
#:package ANcpLua.Roslyn.Utilities.Testing@1.56.1
#addin nuget:?package=ANcpLua.Roslyn.Utilities.Testing&version=1.56.1
#tool nuget:?package=ANcpLua.Roslyn.Utilities.Testing&version=1.56.1
ANcpLua.Roslyn.Utilities
A focused utility library for writing Roslyn source generators, analyzers, and code-fix providers. The shape is opinionated: the types here are the ones that make an incremental generator actually cache, a symbol comparison actually readable, and a code emission actually deterministic.
Agent test doubles, MAF conformance suites, and workflow fixtures live in the sibling ANcpLua.Agents package. This repo focuses on Roslyn/MSBuild/OTel-for-analyzer-authors.
The library is the author's own. Use it, improve it (the upstream is this repo), or propose switching directions — don't reinvent locally.
Packages
| Package | Target | What it's for |
|---|---|---|
ANcpLua.Roslyn.Utilities |
netstandard2.0 | Runtime + Roslyn utilities as a normal NuGet reference |
ANcpLua.Roslyn.Utilities.Sources |
source-only | Embeds the utilities as internal source into source generators (generators can't load NuGet DLLs at runtime) |
ANcpLua.Roslyn.Utilities.Polyfills |
source-only | init, required, Index/Range, nullable & trim attributes for netstandard2.0 |
ANcpLua.Roslyn.Utilities.Testing |
net10.0 | Generator/analyzer/codefix test infrastructure, MSBuild/NuGet integration tests, cross-framework web testing (xUnit/NUnit/TUnit/Bunit), OTel instrumentation helpers, BitNetFixture live-LLM integration |
ANcpLua.Roslyn.Utilities.Testing.Aot |
netstandard2.0 | AOT/trim test attributes ([AotTest], [TrimTest], [AotSafe], [TrimSafe]), TrimAssert, FeatureSwitches, MSBuild orchestration for standalone AOT verification |
Quickstart — writing a source generator
dotnet add package ANcpLua.Roslyn.Utilities.Sources
A canonical incremental generator is ~25 lines when the utilities do their job — see src/ANcpLua.AotReflection/AotReflectionGenerator.cs in this repo for the reference shape:
[Generator]
public sealed class MyGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var flows = context.SyntaxProvider.ForAttributeWithMetadataName(
"MyNamespace.TracedAttribute",
static (node, _) => node is ClassDeclarationSyntax,
static (ctx, ct) => TypeExtractor.Extract(ctx, ct));
var types = flows.ReportAndStop(context);
var files = types
.Select(static (m, _) => OutputGenerator.Generate(m))
.CollectAsEquatableArray();
files.AddSources(context);
}
}
What the library provides at each step:
ForAttributeWithMetadataName— filter syntax before expensive semantic work (Roslyn built-in; the library wraps for ergonomics)DiagnosticFlow<T>— railway-oriented pipeline carrying value + accumulated diagnostics, value-equatable for incremental cachingReportAndStop— drain the flow intoReportDiagnosticand yield valuesCollectAsEquatableArray/ToEquatableArray/ToEquatableArrayOrDefault— value-equatable collections so the generator cache actually hitsAddSources— emitFileWithNamerecords without boilerplateMatch.Type()/Match.Method()— fluent symbol matching, no string comparisonsIndentedStringBuilder—BeginBlock()/EndBlock()scopes, no hardcoded indentation stringsGeneratedCodeHelpers—<auto-generated/>headers,#nullable enable, pragma helpers
Testing an agent or a MAF workflow
Agent test doubles (FakeChatClient, FakeReplayAgent, ActivityCollector), the provider-agnostic MAF conformance suite (RunTests, RunStreamingTests, ChatClientAgentRunTests, StructuredOutputRunTests), reference fixtures for OpenAI / Azure / Anthropic / Ollama / Gemini / OpenRouter, and the WorkflowFixture<TInput> harness ship in the sibling ANcpLua.Agents package.
What's in the box
Incremental pipeline (generator authors)
DiagnosticFlow<T>+DiagnosticFlowReportingExtensions— railway-oriented flow with value + accumulatedDiagnosticInfoIncrementalValuesProviderExtensions—ReportAndStop,CollectAsEquatableArray,AddSources, and other pipeline verbsSyntaxValueProviderExtensions— ergonomicForAttributeWithMetadataNamewrappersSourceProductionContextExtensions— context helpersEquatableArray<T>+EquatableArrayExtensions(.ToEquatableArray,.ToEquatableArrayOrDefault,.AsEquatableArray) — value equality so the cache actually hitsFileWithName— record for emitted-file payloadDiagnosticInfo+LocationInfo+EquatableMessageArgs— value-equatable diagnostic carriersResultWithDiagnostics<T>— result payload paired with diagnostics, for pipelines where the value survives errors
Symbol analysis & matching
Matchfluent DSL —Match.Type()/Match.Method()/Match.Property()/Match.Field()/Match.Parameter()with the per-kind matchers underMatching/(MethodMatcher,TypeMatcher, etc.)Invoke/InvocationMatcher— invocation-shape matchingSymbolExtensions,TypeSymbolExtensions,MethodSymbolExtensions,NamespaceExtensions—IsEqualTo,ImplementsInterface,GetAllBaseTypesAndSelf, and the usual suspectsAttributeExtensions—GetAttribute,HasAttribute,TryGetAttributewith constructor-arg accessorsInvocationExtensions/OperationExtensions/OperationHelper—IInvocationOperationanalysis helpersSemanticGuard<T>+SemanticGuard— chain semantic predicates with early-exitSemanticModelExtensions/CompilationExtensions/SyntaxExtensions
Code emission
IndentedStringBuilder+IndentScope—BeginBlock()/EndBlock()scopes, no hardcoded indentationGeneratedCodeHelpers—<auto-generated/>header, nullable/pragma directivesValueStringBuilder— stack-allocatedStringBuilderfor hot pathsSyntaxBuilders(Analyzers/) — fluent builders for common code-fix patterns (ExtensionCall,StaticCall,NameOf)
Analyzer authoring
DiagnosticAnalyzerBase— convention base classDiagnosticCategories/DiagnosticSeverities— category/severity constantsDeprecatedOtelAttributes— 50+ attribute-name mappings synced to OTel GenAI semconv 1.40 (catchesgen_ai.systemand friends during analysis)IncrementalPipelineHelpers— pipeline wiring utilitiesMappingRegistry/TypeCache<TEnum>— cached lookups for analyzer hot paths
Async & time
AsyncSequenceExtensions— LINQ-ish operators overIAsyncEnumerable<T>ParallelAsyncExtensions— channel-based parallel enumeration with ordering controlExpiringCache<TKey,TValue>— TTL cache for analyzer resultsTimeConversions— OTel nanosecond helpers
Dictionary & collection ergonomics
DictionaryExtensions—GetOrInsert(key, context, factory)(closure-free, hot-path),GetOrAdd(key)/GetOrAdd(key, factory)(ergonomic),MapIfAbsent(src, dst, transform)(cross-key copy for vendor-adapter telemetry mappers)EnumerableExtensions— dedup, partition, chunking,MinBy/MaxByListExtensions— in-place mutation helpers
Guards & null handling
Guard— null / range / path / type guardsNullableExtensions—Map-style combinators forT?TryExtensions—TryParseX, dictionaryGetOrNull/GetOrDefault/GetOrElse
Models & OTel enums
SpanKind,SpanStatusCode— OTel enumsPagedResult<T>— standard paginated response shape
Polyfills
Language and API backports for netstandard2.0 consumers (via the separate .Polyfills package):
| Polyfill | Enables | Opt-out |
|---|---|---|
Index / Range |
array[^1], array[1..3] |
InjectIndexRangeOnLegacy=false |
IsExternalInit |
record types, init setters |
InjectIsExternalInitOnLegacy=false |
| Nullable attributes | [NotNull], [MaybeNull], [MemberNotNull], [NotNullWhen] |
InjectNullabilityAttributesOnLegacy=false |
| Trim/AOT attributes | [RequiresUnreferencedCode], [DynamicallyAccessedMembers] |
InjectTrimAttributesOnLegacy=false |
TimeProvider |
Testable time abstraction | InjectTimeProviderPolyfill=false |
Lock |
System.Threading.Lock |
InjectLockPolyfill=false |
required, params collections, CallerArgumentExpression, UnreachableException, StackTraceHidden, ExperimentalAttribute |
C# 11–13 language features on ns2.0 | per-feature MSBuild props |
Disable all at once: <InjectAllPolyfillsOnLegacy>false</InjectAllPolyfillsOnLegacy>
Testing — what ships in .Testing
Generator, analyzer, codefix, refactoring, MSBuild integration, and cross-framework web testing.
- Generator tests:
Test<TGenerator>(fluent entry),GeneratorTestHelper.RunGenerator<TGenerator>()(assertion-oriented one-liners),GeneratorCachingReport(compares two runs, detects cache hits),Compile/CompileResult(dynamic compilation with Shouldly-style assertions) - Analyzer / CodeFix / Refactoring tests:
AnalyzerTest<TAnalyzer>,CodeFixTest<TAnalyzer,TCodeFix>,CodeFixTestWithEditorConfig<TAnalyzer,TCodeFix>,RefactoringTest<TRefactoring>,SolutionRefactoringTest<TRefactoring>,LogAssert - MSBuild integration tests:
ProjectBuilder,PackageProjectBuilder,PackageTestBase<TFixture>/NuGetPackageTestBase,NuGetPackageFixture,BuildResult+BuildResultAssertions,SarifFileparsers,DotNetSdkHelpers/NetSdkVersion,MSBuildConstants - Web testing — one base per framework:
- xUnit (root):
IntegrationTestBase<TProgram>,KestrelTestBase<TProgram> WebTesting/NUnit/,WebTesting/TUnit/,WebTesting/Bunit/— per-framework equivalents- Plus
FakeLoggerExtensionsfor structured log capture
- xUnit (root):
- OTel instrumentation:
ActivityInstrumentation,MetricsInstrumentation,LoggingConventions,LogEnricherInfrastructure,DataClassificationHelpers(PII/Secret redaction) - Live-LLM integration:
BitNetFixture,BitNetAttribute,BitNetTestGroup— xUnit v3 collection fixture for llama.cpp BitNet server
AOT testing — what ships in .Testing.Aot
Separate package (netstandard2.0, zero runtime deps). Attributes + MSBuild props for verifying AOT/trim behavior.
[AotTest],[AotSafe],[AotUnsafe]— mark methods as AOT-verified or AOT-excluded[TrimTest],[TrimSafe],[TrimUnsafe]— same for IL trimming;TrimModeenum selectsPartial/FullTrimAssert.TypePreserved(...)/TypeRemoved(...)— runtime assertions against the trimmed binaryFeatureSwitches— constants for common AOT feature switches (e.g.JsonReflection)AotRuntime— runtime AOT/trim detection- MSBuild orchestration (
build/*.props+*.targets+ProjectTemplate.csproj.txt) ships inside the package itself; no external SDK dependency
Separate: AOT reflection generator
This repo also ships ANcpLua.Analyzers.AotReflection — an incremental source generator that replaces runtime typeof(T).GetProperties() with compile-time metadata, making types NativeAOT- and trim-safe. Mark a type [AotReflection] and a generated FooMetadata class appears with strongly-typed getter/setter/invoker delegates. Pair with ANcpLua.Roslyn.Utilities.Testing.Aot to verify the output survives PublishAot=true and PublishTrimmed=true.
Design principles
- Compile-time over runtime reflection.
EquatableArray<T>for generator caching,DiagnosticFlow<T>for pipeline error accumulation,IndentedStringBuilderfor emission — the shape of an incremental generator the compiler cache actually likes. - Allocation-conscious in analyzer hot paths.
ValueStringBuilderfor stack-allocated string building,Boxesfor cached boxed primitives, closure-freeGetOrInsert<TContext>for dictionary inserts. - OTel semconv 1.40 native.
DeprecatedOtelAttributescatchesgen_ai.systemand 50+ other deprecated attribute names during analysis. - Test discipline.
GeneratorCachingReportover hand-rolled cache assertions.Test<TGenerator>fluent runner over hand-rolledCSharpGeneratorDriverplumbing. The shipped-internallyForbiddenTypeAnalyzercatchesISymbol/Compilationsnuck into pipeline models. Agent-specific test doubles live in the siblingANcpLua.Agentspackage. - Source-only delivery for generators. The
.Sourcespackage rewrites the utilities tointernalvia a pack-time PowerShell transform (Transform-Sources.ps1), because source generators can't load NuGet DLLs at runtime.
Documentation
ancplua.mintlify.app/utilities — full API reference with examples.
Related
- ANcpLua.NET.Sdk — opinionated .NET SDK wrapper built on the same conventions
- ANcpLua.Analyzers — the author's analyzer catalogue, built on these utilities
- ANcpLua.Agents — agent test doubles, MAF conformance, workflow fixtures
License
MIT © Alexander Nachtmann
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ANcpLua.Roslyn.Utilities (>= 1.56.1)
- AwesomeAssertions (>= 9.4.0)
- Basic.Reference.Assemblies.Net100 (>= 1.8.5)
- Basic.Reference.Assemblies.NetStandard20 (>= 1.8.5)
- bunit (>= 2.7.2)
- Meziantou.Framework (>= 5.0.15)
- Meziantou.Framework.FullPath (>= 1.1.18)
- Meziantou.Framework.TemporaryDirectory (>= 1.0.37)
- Meziantou.Framework.Threading (>= 2.0.4)
- Microsoft.AspNetCore.Mvc.Testing (>= 10.0.6)
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.6)
- Microsoft.Bcl.HashCode (>= 6.0.0)
- Microsoft.CodeAnalysis.Analyzer.Testing (>= 1.1.3)
- Microsoft.CodeAnalysis.Analyzers (>= 5.3.0)
- Microsoft.CodeAnalysis.CSharp (>= 5.3.0)
- Microsoft.CodeAnalysis.CSharp.Analyzer.Testing (>= 1.1.3)
- Microsoft.CodeAnalysis.CSharp.CodeFix.Testing (>= 1.1.3)
- Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing (>= 1.1.3)
- Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing (>= 1.1.3)
- Microsoft.CodeAnalysis.CSharp.Workspaces (>= 5.3.0)
- Microsoft.CodeAnalysis.Workspaces.Common (>= 5.3.0)
- Microsoft.Deployment.DotNet.Releases (>= 1.0.1)
- Microsoft.Extensions.AI.Abstractions (>= 10.5.0)
- Microsoft.Extensions.Diagnostics.Testing (>= 10.5.0)
- Microsoft.Extensions.Telemetry.Abstractions (>= 10.5.0)
- MSBuild.StructuredLogger (>= 2.3.154)
- NUnit (>= 4.5.1)
- OpenAI (>= 2.10.0)
- TUnit.Core (>= 1.34.5)
- xunit.v3.assert (>= 3.2.2)
- xunit.v3.extensibility.core (>= 3.2.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.56.1 | 0 | 4/21/2026 |
| 1.56.0 | 0 | 4/21/2026 |
| 1.55.0 | 0 | 4/21/2026 |
| 1.54.1 | 0 | 4/20/2026 |
| 1.54.0 | 0 | 4/20/2026 |
| 1.53.3 | 54 | 4/20/2026 |
| 1.53.2 | 32 | 4/19/2026 |
| 1.53.1 | 34 | 4/19/2026 |
| 1.53.0 | 229 | 4/18/2026 |
| 1.52.1 | 38 | 4/18/2026 |
| 1.52.0 | 159 | 4/15/2026 |
| 1.51.1 | 146 | 4/12/2026 |
| 1.51.0 | 251 | 4/3/2026 |
| 1.50.0 | 98 | 4/3/2026 |
| 1.49.0 | 98 | 4/2/2026 |
| 1.48.0 | 209 | 3/20/2026 |
| 1.47.0 | 205 | 3/12/2026 |
| 1.45.0 | 378 | 3/10/2026 |
| 1.43.0 | 250 | 2/28/2026 |
| 1.42.0 | 226 | 2/28/2026 |