From ed286b1c83158da1cef2e1fdffa28cd629e66bcf Mon Sep 17 00:00:00 2001 From: dvolper Date: Thu, 23 May 2024 08:27:07 +0200 Subject: [PATCH] cref resolving concept & improve snapshot tests --- Doki.sln | 14 +++++ src/Doki.Abstractions/MemberDocumentation.cs | 5 ++ .../TypeDocumentationReference.cs | 5 -- src/Doki/DocumentationGenerator.Content.cs | 51 +++++++++++++++--- .../Doki.Tests.Common.csproj | 14 +++++ tests/Doki.Tests.Common/TestOutputLogger.cs | 29 ++++++++++ .../Doki.Tests.Snapshots.csproj | 2 + tests/Doki.Tests.Snapshots/SnapshotTests.cs | 53 +++++++++++++++++-- .../Doki.TestAssembly/README.md | 6 +++ .../Doki.TestAssembly/README.md | 7 +++ .../Test_ClassWithPropertyRef/README.md | 6 +++ .../Doki.TestAssembly/ClassWithPropertyRef.cs | 14 +++++ .../Doki.TestAssembly.csproj | 10 ++++ 13 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 tests/Doki.Tests.Common/Doki.Tests.Common.csproj create mode 100644 tests/Doki.Tests.Common/TestOutputLogger.cs create mode 100644 tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/Doki.TestAssembly/README.md create mode 100644 tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/README.md create mode 100644 tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/README.md create mode 100644 tests/assemblies/Doki.TestAssembly/ClassWithPropertyRef.cs create mode 100644 tests/assemblies/Doki.TestAssembly/Doki.TestAssembly.csproj diff --git a/Doki.sln b/Doki.sln index f9f62a9..de5371a 100644 --- a/Doki.sln +++ b/Doki.sln @@ -30,6 +30,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.Output.Json", "src\Dok EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.Output.Json.Tests", "tests\Doki.Output.Json.Tests\Doki.Output.Json.Tests.csproj", "{6CCD9EE6-B3FC-485F-9155-553165141B20}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.TestAssembly", "tests\assemblies\Doki.TestAssembly\Doki.TestAssembly.csproj", "{0293D689-DFDC-4A78-80D8-BFC11DB0A175}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Doki.Tests.Common", "tests\Doki.Tests.Common\Doki.Tests.Common.csproj", "{0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +50,8 @@ Global {A89D22B2-2427-4863-A2D9-9E1BEFF37C61} = {568576F3-3D48-459E-B4D2-1790DAE80E7A} {00BCCBC0-A719-489C-A746-559B4D055B56} = {568576F3-3D48-459E-B4D2-1790DAE80E7A} {6CCD9EE6-B3FC-485F-9155-553165141B20} = {8C7B5305-B599-4F08-B28B-DD9F1715DD51} + {0293D689-DFDC-4A78-80D8-BFC11DB0A175} = {08041208-BE3D-4BE8-9AF7-806B73985275} + {0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC} = {8C7B5305-B599-4F08-B28B-DD9F1715DD51} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6F31B87A-2BD3-4FB4-8C08-7E059A338D4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -96,5 +102,13 @@ Global {6CCD9EE6-B3FC-485F-9155-553165141B20}.Debug|Any CPU.Build.0 = Debug|Any CPU {6CCD9EE6-B3FC-485F-9155-553165141B20}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CCD9EE6-B3FC-485F-9155-553165141B20}.Release|Any CPU.Build.0 = Release|Any CPU + {0293D689-DFDC-4A78-80D8-BFC11DB0A175}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0293D689-DFDC-4A78-80D8-BFC11DB0A175}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0293D689-DFDC-4A78-80D8-BFC11DB0A175}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0293D689-DFDC-4A78-80D8-BFC11DB0A175}.Release|Any CPU.Build.0 = Release|Any CPU + {0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0FA0FF7A-6EDA-4CB1-8D81-2DFB1A4077CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Doki.Abstractions/MemberDocumentation.cs b/src/Doki.Abstractions/MemberDocumentation.cs index 3efd68e..48bd92b 100644 --- a/src/Doki.Abstractions/MemberDocumentation.cs +++ b/src/Doki.Abstractions/MemberDocumentation.cs @@ -25,5 +25,10 @@ public record MemberDocumentation : DocumentationObject /// public XmlDocumentation? Summary { get; set; } + /// + /// Gets a value indicating whether the type is documented. + /// + public bool IsDocumented { get; init; } + public new DocumentationContentType ContentType { get; init; } } \ No newline at end of file diff --git a/src/Doki.Abstractions/TypeDocumentationReference.cs b/src/Doki.Abstractions/TypeDocumentationReference.cs index 9d97116..35ffd95 100644 --- a/src/Doki.Abstractions/TypeDocumentationReference.cs +++ b/src/Doki.Abstractions/TypeDocumentationReference.cs @@ -15,11 +15,6 @@ public record TypeDocumentationReference : MemberDocumentation /// public string FullName { get; init; } = null!; - /// - /// Gets a value indicating whether the type is documented. - /// - public bool IsDocumented { get; init; } - /// /// Gets a value indicating whether the type is from Microsoft. /// diff --git a/src/Doki/DocumentationGenerator.Content.cs b/src/Doki/DocumentationGenerator.Content.cs index eb4074c..eb516b4 100644 --- a/src/Doki/DocumentationGenerator.Content.cs +++ b/src/Doki/DocumentationGenerator.Content.cs @@ -132,6 +132,7 @@ private IEnumerable BuildFieldDocumentation(Type type, Docu Namespace = field.DeclaringType.Namespace, Assembly = fieldAssembly.Name, Parent = parent, + IsDocumented = true }; if (summary != null) @@ -173,6 +174,7 @@ private IEnumerable BuildConstructorDocumentation(Type type Namespace = constructor.DeclaringType.Namespace, Assembly = constructorAssembly.Name, Parent = parent, + IsDocumented = true }; if (summary != null) @@ -214,6 +216,7 @@ private IEnumerable BuildPropertyDocumentation(Type type, D Namespace = property.DeclaringType.Namespace, Assembly = propertyAssembly.Name, Parent = parent, + IsDocumented = true }; if (summary != null) @@ -257,6 +260,7 @@ private IEnumerable BuildMethodDocumentation(Type type, Doc Namespace = method.DeclaringType.Namespace, Assembly = methodAssembly.Name, Parent = parent, + IsDocumented = true }; if (summary != null) @@ -337,13 +341,16 @@ private XmlDocumentation BuildXmlDocumentation(XPathNavigator navigator, Documen private DocumentationObject BuildCRefDocumentation(string cref, DocumentationObject parent) { - if (!cref.StartsWith("T:")) throw new ArgumentOutOfRangeException(nameof(cref), cref, "Unsupported cref."); + var parts = cref.Split(':'); + if (parts.Length != 2) throw new ArgumentOutOfRangeException(nameof(cref), cref, "Invalid cref format."); - var typeName = cref[2..]; - if (!typeName.Contains('.')) + var memberType = parts[0]; + var memberName = parts[1]; + var typeName = parts[1]; + if (memberType != "T") { - var @namespace = parent.TryGetParent(DocumentationContentType.Namespace); - if (@namespace != null) typeName = $"{@namespace.Id}.{typeName}"; + memberName = memberName.Split('.').Last(); + typeName = typeName[..typeName.LastIndexOf('.')]; } var type = LookupType(typeName); @@ -352,10 +359,40 @@ private DocumentationObject BuildCRefDocumentation(string cref, DocumentationObj { Id = "text", Parent = parent, - Text = typeName + Text = memberName }; - return BuildTypeDocumentationReference(type, parent); + var typeDocumentationReference = BuildTypeDocumentationReference(type, parent); + + switch (memberType) + { + case "T": + return typeDocumentationReference; + case "P": + { + var property = type.GetProperty(memberName, AllMembersBindingFlags); + if (property == null) + return new TextContent + { + Id = "text", + Parent = parent, + Text = memberName + }; + + return new MemberDocumentation + { + Id = property.GetXmlDocumentationId(), + Name = property.Name, + ContentType = DocumentationContentType.Property, + Namespace = type.Namespace, + Assembly = type.Assembly.GetName().Name, + Parent = typeDocumentationReference, + IsDocumented = PropertyFilter.Expression?.Invoke(property) ?? PropertyFilter.Default(property) + }; + } + default: + throw new ArgumentOutOfRangeException(nameof(cref), cref, "Unsupported cref member type."); + } } private TypeDocumentationReference BuildTypeDocumentationReference(Type type, DocumentationObject parent) diff --git a/tests/Doki.Tests.Common/Doki.Tests.Common.csproj b/tests/Doki.Tests.Common/Doki.Tests.Common.csproj new file mode 100644 index 0000000..c367fb7 --- /dev/null +++ b/tests/Doki.Tests.Common/Doki.Tests.Common.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/tests/Doki.Tests.Common/TestOutputLogger.cs b/tests/Doki.Tests.Common/TestOutputLogger.cs new file mode 100644 index 0000000..1817fb4 --- /dev/null +++ b/tests/Doki.Tests.Common/TestOutputLogger.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Doki.Tests.Common; + +public sealed class TestOutputLogger(ITestOutputHelper output) : ILogger +{ + public bool HadError { get; private set; } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter) + { + if (logLevel == LogLevel.Error) HadError = true; + + output.WriteLine(formatter(state, exception)); + + if (exception != null) output.WriteLine(exception.ToString()); + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public IDisposable? BeginScope(TState state) where TState : notnull + { + return null; + } +} \ No newline at end of file diff --git a/tests/Doki.Tests.Snapshots/Doki.Tests.Snapshots.csproj b/tests/Doki.Tests.Snapshots/Doki.Tests.Snapshots.csproj index 478e1b7..f3fe776 100644 --- a/tests/Doki.Tests.Snapshots/Doki.Tests.Snapshots.csproj +++ b/tests/Doki.Tests.Snapshots/Doki.Tests.Snapshots.csproj @@ -27,6 +27,8 @@ + + diff --git a/tests/Doki.Tests.Snapshots/SnapshotTests.cs b/tests/Doki.Tests.Snapshots/SnapshotTests.cs index b2afd2f..6b514d5 100644 --- a/tests/Doki.Tests.Snapshots/SnapshotTests.cs +++ b/tests/Doki.Tests.Snapshots/SnapshotTests.cs @@ -4,7 +4,7 @@ using Doki.TestAssembly; using Doki.TestAssembly.InheritanceChain; using Doki.TestAssembly.InheritanceChain.Abstractions; -using Microsoft.Extensions.Logging.Abstractions; +using Doki.Tests.Common; using Xunit.Abstractions; namespace Doki.Tests.Snapshots; @@ -27,9 +27,13 @@ public async Task Test_RootNamespaceIsParentNamespace() OutputDirectory = snapshot.OutputDirectory })); - await generator.GenerateAsync(NullLogger.Instance); + var logger = new TestOutputLogger(testOutputHelper); + + await generator.GenerateAsync(logger); await snapshot.SaveIfNotExists().MatchSnapshotAsync(testOutputHelper); + + Assert.False(logger.HadError); } [Fact] @@ -49,8 +53,51 @@ public async Task Test_InheritanceChain() OutputDirectory = snapshot.OutputDirectory })); - await generator.GenerateAsync(NullLogger.Instance); + var logger = new TestOutputLogger(testOutputHelper); + + await generator.GenerateAsync(logger); await snapshot.SaveIfNotExists().MatchSnapshotAsync(testOutputHelper); + + Assert.False(logger.HadError); + } + + [Fact] + public async Task Test_ClassWithPropertyRef() + { + var snapshot = Snapshot.Create(); + + var emptyDocumentation = new XPathDocument(new StringReader(""" + + + + Doki.TestAssembly + + + + + Does something with . + + + + + """)); + + var generator = new DocumentationGenerator(); + + generator.AddAssembly(typeof(ClassWithPropertyRef).Assembly, emptyDocumentation); + + generator.AddOutput(new MarkdownOutput(new OutputOptions + { + OutputDirectory = snapshot.OutputDirectory + })); + + var logger = new TestOutputLogger(testOutputHelper); + + await generator.GenerateAsync(logger); + + await snapshot.SaveIfNotExists().MatchSnapshotAsync(testOutputHelper); + + Assert.False(logger.HadError); } } \ No newline at end of file diff --git a/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/Doki.TestAssembly/README.md b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/Doki.TestAssembly/README.md new file mode 100644 index 0000000..24a1d61 --- /dev/null +++ b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/Doki.TestAssembly/README.md @@ -0,0 +1,6 @@ +# Doki.TestAssembly Namespace + +## Types + + + diff --git a/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/README.md b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/README.md new file mode 100644 index 0000000..5157503 --- /dev/null +++ b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/Doki.TestAssembly/README.md @@ -0,0 +1,7 @@ +# Doki.TestAssembly + +## Namespaces + +- [Doki.TestAssembly](Doki.TestAssembly/README.md) + + diff --git a/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/README.md b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/README.md new file mode 100644 index 0000000..dc38c84 --- /dev/null +++ b/tests/Doki.Tests.Snapshots/__snapshots__/Test_ClassWithPropertyRef/README.md @@ -0,0 +1,6 @@ +# Packages + +- [Doki.TestAssembly](Doki.TestAssembly/README.md) + + + diff --git a/tests/assemblies/Doki.TestAssembly/ClassWithPropertyRef.cs b/tests/assemblies/Doki.TestAssembly/ClassWithPropertyRef.cs new file mode 100644 index 0000000..bc5d0a9 --- /dev/null +++ b/tests/assemblies/Doki.TestAssembly/ClassWithPropertyRef.cs @@ -0,0 +1,14 @@ +namespace Doki.TestAssembly; + +public class ClassWithPropertyRef +{ + public string? Property { get; set; } + + /// + /// Does something with . + /// + public void Method() + { + var property = Property; + } +} \ No newline at end of file diff --git a/tests/assemblies/Doki.TestAssembly/Doki.TestAssembly.csproj b/tests/assemblies/Doki.TestAssembly/Doki.TestAssembly.csproj new file mode 100644 index 0000000..bd9b062 --- /dev/null +++ b/tests/assemblies/Doki.TestAssembly/Doki.TestAssembly.csproj @@ -0,0 +1,10 @@ + + + + net8.0 + enable + enable + True + + +