Skip to content

Commit

Permalink
Pack readme inside nuget package
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalBrylka committed Jul 14, 2023
1 parent 40f1b5d commit 56ac87f
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 170 deletions.
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<Authors>Michał Bryłka, Leszek Kowalski</Authors>
<PackageLicenseExpression>MIT OR Apache-2.0</PackageLicenseExpression>
<Copyright>Copyright (c) Michał Bryłka. Icon by http://www.iconka.com </Copyright>
<IsPackable>false</IsPackable>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<PackageProjectUrl>https://github.com/nemesissoft/Nemesis.TextParsers</PackageProjectUrl>

<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ namespace Nemesis.TextParsers.CodeGen.Tests.ApprovalTests
internal class AutoDeconstructableGeneratorApprovalTests
{
[Test] public void ApprovalTestsRecord() => RunCase("Record");

[Test] public void ApprovalTestsStruct() => RunCase("ReadOnlyStruct");

[Test] public void ApprovalTestsLarge() => RunCase("Large");

[Test] public void ApprovalTestsComplexTypes() => RunCase("ComplexType");

[Test] public void ApprovalTestsSimpleWrapperStruct() => RunCase("SimpleWrapperStruct");



[Test]
public void HouseKeeping() => ApprovalMaintenance.VerifyNoAbandonedFiles();

Expand All @@ -34,11 +34,11 @@ private static void RunCase(string index)
var (_, source, _) = EndToEndCases.AutoDeconstructableCases().SingleOrDefault(t => t.name == index);
Assert.That(source, Is.Not.Null);
Assert.That(source, Is.Not.Empty);

var compilation = CreateCompilation(
$@"using Nemesis.TextParsers.Settings; namespace Nemesis.TextParsers.CodeGen.Tests {{ {source} }}");

var generatedTrees = AutoDeconstructableGeneratorTests.GetGeneratedTreesOnly(compilation);
var generatedTrees = GetGeneratedTreesOnly(compilation);

var actual = ScrubGeneratorComments(generatedTrees.Single());

Expand Down
136 changes: 50 additions & 86 deletions Nemesis.TextParsers.CodeGen.Tests/AutoDeconstructableGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -1,45 +1,38 @@
extern alias original;
using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Nemesis.CodeAnalysis;
using Nemesis.TextParsers.CodeGen.Deconstructable;

using NUnit.Framework;

using static Nemesis.TextParsers.CodeGen.Tests.Utils;

namespace Nemesis.TextParsers.CodeGen.Tests;

namespace Nemesis.TextParsers.CodeGen.Tests
[TestFixture]
public partial class AutoDeconstructableGeneratorTests
{
[TestFixture]
public partial class AutoDeconstructableGeneratorTests
{
private static readonly IEnumerable<TestCaseData> _endToEndCases = EndToEndCases.AutoDeconstructableCases()
.Select((t, i) => new TestCaseData($@"
private static readonly IEnumerable<TestCaseData> _endToEndCases = EndToEndCases.AutoDeconstructableCases()
.Select((t, i) => new TestCaseData($@"
using Nemesis.TextParsers.Settings;
namespace Nemesis.TextParsers.CodeGen.Tests {{ {t.source} }}", t.expectedCode)
.SetName($"E2E_{i + 1:00}_{t.name}"));
.SetName($"E2E_{i + 1:00}_{t.name}"));

[TestCaseSource(nameof(_endToEndCases))]
public void EndToEndTests(string source, string expectedCode)
{
var compilation = CreateCompilation(source);
[TestCaseSource(nameof(_endToEndCases))]
public void EndToEndTests(string source, string expectedCode)
{
var compilation = CreateCompilation(source);

var generatedTrees = GetGeneratedTreesOnly(compilation);
var generatedTrees = GetGeneratedTreesOnly(compilation);

var actual = ScrubGeneratorComments(generatedTrees.Single());
var actual = ScrubGeneratorComments(generatedTrees.Single());

Assert.That(actual, Is.EqualTo(expectedCode).Using(IgnoreNewLinesComparer.EqualityComparer));
}
Assert.That(actual, Is.EqualTo(expectedCode).Using(IgnoreNewLinesComparer.EqualityComparer));
}


private static readonly IEnumerable<TestCaseData> _settingsCases = new (string typeDefinition, string expectedCodePart)[]
{
(@"[DeconstructableSettings(':', '␀', '/', '{', '}')]
private static readonly IEnumerable<TestCaseData> _settingsCases = new (string typeDefinition, string expectedCodePart)[]
{
(@"[DeconstructableSettings(':', '␀', '/', '{', '}')]
readonly partial struct Child
{
public byte Age { get; }
Expand All @@ -49,87 +42,59 @@ readonly partial struct Child
}", "private readonly TupleHelper _helper = new TupleHelper(':', '␀', '/', '{', '}');"),


(@"[DeconstructableSettings('_', '␀', '*', '<', '>')]
(@"[DeconstructableSettings('_', '␀', '*', '<', '>')]
partial record T(byte B) { }", @"new TupleHelper('_', '␀', '*', '<', '>');"),

(@"[DeconstructableSettings('_', '␀', '*', '<')]
(@"[DeconstructableSettings('_', '␀', '*', '<')]
partial record T(byte B) { }", @"new TupleHelper('_', '␀', '*', '<', ')');"),

(@"[DeconstructableSettings('_', '␀', '*')]
(@"[DeconstructableSettings('_', '␀', '*')]
partial record T(byte B) { }", @"new TupleHelper('_', '␀', '*', '(', ')');"),

(@"[DeconstructableSettings('_', '␀')]
(@"[DeconstructableSettings('_', '␀')]
partial record T(byte B) { }", @"new TupleHelper('_', '␀', '\\', '(', ')');"),

(@"[DeconstructableSettings('_')]
(@"[DeconstructableSettings('_')]
partial record T(byte B) { }", @"new TupleHelper('_', '∅', '\\', '(', ')');"),

(@"[DeconstructableSettings]
(@"[DeconstructableSettings]
partial record T(byte B) { }", @"new TupleHelper(';', '∅', '\\', '(', ')');"),

(@"partial record T(byte B) { }", @"_helper = transformerStore.SettingsStore.GetSettingsFor<Nemesis.TextParsers.Settings.DeconstructableSettings>().ToTupleHelper();"),
(@"partial record T(byte B) { }", @"_helper = transformerStore.SettingsStore.GetSettingsFor<Nemesis.TextParsers.Settings.DeconstructableSettings>().ToTupleHelper();"),


(@"[DeconstructableSettings(useDeconstructableEmpty: true)]
(@"[DeconstructableSettings(useDeconstructableEmpty: true)]
partial record T(byte B) { }", @"public override T GetEmpty() => new T(_transformer_B.GetEmpty());"),

(@"[DeconstructableSettings(useDeconstructableEmpty: false)]
(@"[DeconstructableSettings(useDeconstructableEmpty: false)]
partial record T(byte B) { }", @"NOT CONTAIN:GetEmpty()"),
}
.Select((t, i) => new TestCaseData($@"using Nemesis.TextParsers.Settings; namespace Tests {{ [Auto.AutoDeconstructable] {t.typeDefinition} }}", t.expectedCodePart)
.SetName($"Settings{i + 1:00}"));

[TestCaseSource(nameof(_settingsCases))]
public void SettingsRetrieval_ShouldEmitProperValues(string source, string expectedCodePart)
{
bool matchNotContain = expectedCodePart.StartsWith("NOT CONTAIN:");
if (matchNotContain)
expectedCodePart = expectedCodePart.Substring(12);

//arrange
var compilation = CreateCompilation(source);

//act
var generatedTrees = GetGeneratedTreesOnly(compilation);
var actual = generatedTrees.Single();
}
.Select((t, i) => new TestCaseData($@"using Nemesis.TextParsers.Settings; namespace Tests {{ [Auto.AutoDeconstructable] {t.typeDefinition} }}", t.expectedCodePart)
.SetName($"Settings{i + 1:00}"));

[TestCaseSource(nameof(_settingsCases))]
public void SettingsRetrieval_ShouldEmitProperValues(string source, string expectedCodePart)
{
bool matchNotContain = expectedCodePart.StartsWith("NOT CONTAIN:");
if (matchNotContain)
expectedCodePart = expectedCodePart.Substring(12);

//assert
Assert.That(actual, matchNotContain ? Does.Not.Contain(expectedCodePart) : Does.Contain(expectedCodePart));
}
//arrange
var compilation = CreateCompilation(source);

public static IReadOnlyList<string> GetGeneratedTreesOnly(Compilation compilation, int requiredCardinality = 1)
{
var newComp = CompilationUtils.RunGenerators(compilation, out var diagnostics, new AutoDeconstructableGenerator());
Assert.That(diagnostics, Is.Empty);

SyntaxTree attributeTree = null;
foreach (var tree in newComp.SyntaxTrees)
{
var attributeDeclaration = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
.FirstOrDefault(cds => string.Equals(cds.Identifier.ValueText, AutoDeconstructableGenerator.ATTRIBUTE_NAME, StringComparison.Ordinal));
if (attributeDeclaration != null)
{
attributeTree = tree;
break;
}
}
Assert.That(attributeTree, Is.Not.Null, "Auto attribute not found among generated trees");
//act
var generatedTrees = GetGeneratedTreesOnly(compilation);
var actual = generatedTrees.Single();

var toRemove = compilation.SyntaxTrees.Append(attributeTree);

var generatedTrees = newComp.RemoveSyntaxTrees(toRemove).SyntaxTrees.ToList();
Assert.That(generatedTrees, Has.Count.EqualTo(requiredCardinality));

return generatedTrees.Select(tree =>
((CompilationUnitSyntax)tree.GetRoot())
.ToFullString()).ToList();
}
//assert
Assert.That(actual, matchNotContain ? Does.Not.Contain(expectedCodePart) : Does.Contain(expectedCodePart));
}

[Test]
public void Generate_When_StaticUsing_And_Mnemonics()
{
var compilation = CreateCompilation(@"using SD = System.Double;
[Test]
public void Generate_When_StaticUsing_And_Mnemonics()
{
var compilation = CreateCompilation(@"using SD = System.Double;
using static Tests3.ContainerClass3;
namespace Tests1
Expand All @@ -148,11 +113,11 @@ namespace Tests3
public static class ContainerClass3 { public class NestedClass3 { } }
}");

var generatedTrees = GetGeneratedTreesOnly(compilation);
var generatedTrees = GetGeneratedTreesOnly(compilation);

var actual = ScrubGeneratorComments(generatedTrees.Single());
var actual = ScrubGeneratorComments(generatedTrees.Single());

Assert.That(actual, Is.EqualTo(@"//HEAD
Assert.That(actual, Is.EqualTo(@"//HEAD
using System;
using Nemesis.TextParsers;
using Nemesis.TextParsers.Parsers;
Expand Down Expand Up @@ -235,7 +200,6 @@ public override string Format(Numbers element)
}
}
}").Using(IgnoreNewLinesComparer.EqualityComparer));
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions Nemesis.TextParsers.CodeGen.Tests/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Style", "IDE0057:Use range operator", Justification = "Multiple target frameworks without support for range operator", Scope = "namespaceanddescendants", Target = "~N:Nemesis.TextParsers.CodeGen.Tests")]
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
<!--TODO add net48 target-->
<TargetFrameworks>net6.0</TargetFrameworks>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);NU1603</NoWarn>
<IsPackable>false</IsPackable>
<NoWarn>$(NoWarn);NU1603</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
91 changes: 61 additions & 30 deletions Nemesis.TextParsers.CodeGen.Tests/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,89 @@
extern alias original;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text.RegularExpressions;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

using Nemesis.CodeAnalysis;
using Nemesis.TextParsers.CodeGen.Deconstructable;
using NUnit.Framework;

namespace Nemesis.TextParsers.CodeGen.Tests
namespace Nemesis.TextParsers.CodeGen.Tests;

internal static class Utils
{
internal static class Utils
public static Compilation CreateCompilation(string source, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary)
{
public static Compilation CreateCompilation(string source, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary)
{
var (compilation, _, _) = CompilationUtils.CreateTestCompilation(source, new[]
{
typeof(BigInteger).GetTypeInfo().Assembly,
typeof(original::Nemesis.TextParsers.ITransformer).GetTypeInfo().Assembly
}, outputKind);
var (compilation, _, _) = CompilationUtils.CreateTestCompilation(source, new[]
{
typeof(BigInteger).GetTypeInfo().Assembly,
typeof(original::Nemesis.TextParsers.ITransformer).GetTypeInfo().Assembly
}, outputKind);

return compilation;
}
return compilation;
}


private static readonly Regex _headerPattern = new(@"/\*\s*<auto-generated> .+? </auto-generated>\s*\*/", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
private static readonly Regex _generatorPattern = new(@""".*Generator""\s*,\s*""([0-9.]+)""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
private static readonly Regex _headerPattern = new(@"/\*\s*<auto-generated> .+? </auto-generated>\s*\*/", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
private static readonly Regex _generatorPattern = new(@""".*Generator""\s*,\s*""([0-9.]+)""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);

public static string ScrubGeneratorComments(string text)
{
text = _generatorPattern.Replace(text, "string.Empty, string.Empty");
text = _headerPattern.Replace(text, "//HEAD");
public static string ScrubGeneratorComments(string text)
{
text = _generatorPattern.Replace(text, "string.Empty, string.Empty");
text = _headerPattern.Replace(text, "//HEAD");

return text;
}

public static IReadOnlyList<string> GetGeneratedTreesOnly(Compilation compilation, int requiredCardinality = 1)
{
var newComp = CompilationUtils.RunGenerators(compilation, out var diagnostics, new AutoDeconstructableGenerator());
Assert.That(diagnostics, Is.Empty);

return text;
SyntaxTree attributeTree = null;
foreach (var tree in newComp.SyntaxTrees)
{
var attributeDeclaration = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()
.FirstOrDefault(cds => string.Equals(cds.Identifier.ValueText, AutoDeconstructableGenerator.ATTRIBUTE_NAME, StringComparison.Ordinal));
if (attributeDeclaration != null)
{
attributeTree = tree;
break;
}
}
Assert.That(attributeTree, Is.Not.Null, "Auto attribute not found among generated trees");

var toRemove = compilation.SyntaxTrees.Append(attributeTree);

var generatedTrees = newComp.RemoveSyntaxTrees(toRemove).SyntaxTrees.ToList();
Assert.That(generatedTrees, Has.Count.EqualTo(requiredCardinality));

return generatedTrees.Select(tree =>
((CompilationUnitSyntax)tree.GetRoot())
.ToFullString()).ToList();
}
}


internal class IgnoreNewLinesComparer : IComparer<string>, IEqualityComparer<string>
{
public static readonly IComparer<string> Comparer = new IgnoreNewLinesComparer();
internal class IgnoreNewLinesComparer : IComparer<string>, IEqualityComparer<string>
{
public static readonly IComparer<string> Comparer = new IgnoreNewLinesComparer();

public static readonly IEqualityComparer<string> EqualityComparer = new IgnoreNewLinesComparer();
public static readonly IEqualityComparer<string> EqualityComparer = new IgnoreNewLinesComparer();

public int Compare(string x, string y) => string.CompareOrdinal(NormalizeNewLines(x), NormalizeNewLines(y));
public int Compare(string x, string y) => string.CompareOrdinal(NormalizeNewLines(x), NormalizeNewLines(y));

public bool Equals(string x, string y) => NormalizeNewLines(x) == NormalizeNewLines(y);
public bool Equals(string x, string y) => NormalizeNewLines(x) == NormalizeNewLines(y);

public int GetHashCode(string s) => NormalizeNewLines(s)?.GetHashCode() ?? 0;
public int GetHashCode(string s) => NormalizeNewLines(s)?.GetHashCode() ?? 0;

public static string NormalizeNewLines(string s) => s?
.Replace(Environment.NewLine, "")
.Replace("\n", "")
.Replace("\r", "");
}
public static string NormalizeNewLines(string s) => s?
.Replace(Environment.NewLine, "")
.Replace("\n", "")
.Replace("\r", "");
}
Loading

0 comments on commit 56ac87f

Please sign in to comment.