Files
ProxyGenerator/tests/ProxyInterfaceSourceGeneratorTests/InheritedInterfaceTests.cs
T
Adam Hathcock a6b99abd13 Generate structs for structs and fix struct out parameter if known (#8)
* Generate structs for structs and fix struct out parameter if known

* fmt
2024-06-17 17:04:52 +01:00

254 lines
8.4 KiB
C#

using CSharp.SourceGenerators.Extensions;
using CSharp.SourceGenerators.Extensions.Models;
using FluentAssertions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using ProxyInterfaceSourceGeneratorTests.Source.Disposable;
using Speckle.ProxyGenerator;
namespace ProxyInterfaceSourceGeneratorTests;
[Flags]
public enum ImplementationOptions
{
None = 0,
ProxyBaseClasses = 1,
ProxyInterfaces = 2,
UseExtendedInterfaces = 4,
ProxyForBaseInterface = 8
}
public class InheritedInterfaceTests
{
private const string Namespace = "ProxyInterfaceSourceGeneratorTests.Source.Disposable";
private const string OutputPath = "../../../Destination/Disposable/";
private readonly ProxyInterfaceCodeGenerator _sut;
public InheritedInterfaceTests()
{
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
_sut = new ProxyInterfaceCodeGenerator();
}
[Theory]
[InlineData(ImplementationOptions.None, false)]
[InlineData(
ImplementationOptions.ProxyBaseClasses | ImplementationOptions.ProxyInterfaces,
true
)]
public void GenerateFiles_InheritedInterface_InheritFromBaseClass(
ImplementationOptions options,
bool inheritBaseInterface
)
{
var name = "Child";
var interfaceName = "I" + name;
var proxyName = name + "Proxy";
// Arrange
string[] fileNames = [$"{Namespace}.{interfaceName}.g.cs", $"{Namespace}.{proxyName}.g.cs"];
var path = $"./Source/Disposable/{interfaceName}.cs";
SourceFile sourceFile = CreateSourceFile(path, name, options);
// Act
var result = _sut.Execute([sourceFile]);
result.Valid.Should().BeTrue();
result.Files.Should().HaveCount(fileNames.Length + 1);
WriteFiles(fileNames, result);
var interfaceIndex = 0;
var tree = result.Files[interfaceIndex].SyntaxTree;
var root = tree.GetRoot();
var interfaceDeclarations = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>();
// Assert
Assert.Single(interfaceDeclarations);
var baseList = interfaceDeclarations.First().BaseList;
bool didWeInherit = baseList is not null;
Assert.Equal(didWeInherit, inheritBaseInterface);
}
[Theory]
[InlineData("Parent")]
[InlineData("Child")]
public void GenerateFiles_InheritedInterface_Should_InheritTheInterface(string name)
{
var interfaceName = "I" + name;
var proxyName = name + "Proxy";
// Arrange
string[] fileNames = [$"{Namespace}.{interfaceName}.g.cs", $"{Namespace}.{proxyName}.g.cs"];
var path = $"./Source/Disposable/{interfaceName}.cs";
SourceFile sourceFile = CreateSourceFile(
path,
name,
ImplementationOptions.ProxyInterfaces
| ImplementationOptions.ProxyBaseClasses
| ImplementationOptions.UseExtendedInterfaces
);
// Act
var result = _sut.Execute([sourceFile]);
result.Valid.Should().BeTrue();
result.Files.Should().HaveCount(fileNames.Length + 1);
WriteFiles(fileNames, result);
var interfaceIndex = 0;
var tree = result.Files[interfaceIndex].SyntaxTree;
var root = tree.GetRoot();
var interfaceDeclarations = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>();
// Assert
Assert.Single(interfaceDeclarations);
var baseList = interfaceDeclarations.First().BaseList;
Assert.Equal(2, baseList?.Types.Count);
var type1 = (QualifiedNameSyntax)baseList!.Types[0].Type;
var type2 = (QualifiedNameSyntax)baseList.Types[1].Type;
Assert.Equal(nameof(IDisposable), type1.Right.Identifier.Text);
Assert.Equal(nameof(IUpdate<string>), type2.Right.Identifier.Text);
}
[Fact]
public void GenerateFiles_InheritedInterface_Should_InheritTheInterfaceAndNotNew()
{
var className1 = "LocationPoint";
var interfaceName1 = "IRevitLocationPointProxy";
var proxyName1 = $"{className1}Proxy";
// Arrange
var path1 = $"./Source/Disposable/{interfaceName1}.cs";
var sourceFile1 = CreateSourceFile(
path1,
className1,
ImplementationOptions.ProxyForBaseInterface
| ImplementationOptions.UseExtendedInterfaces
);
var className2 = "Location";
var interfaceName2 = "IRevitLocationProxy";
var proxyName2 = $"{className2}Proxy";
// Arrange
var path2 = $"./Source/Disposable/{interfaceName2}.cs";
var sourceFile2 = CreateSourceFile(
path2,
className2,
ImplementationOptions.ProxyForBaseInterface
| ImplementationOptions.UseExtendedInterfaces
);
// Act
var result = _sut.Execute([sourceFile1, sourceFile2]);
string[] fileNames =
[
$"{Namespace}.{interfaceName1}.g.cs",
$"{Namespace}.{proxyName1}.g.cs",
$"{Namespace}.{interfaceName2}.g.cs",
$"{Namespace}.{proxyName2}.g.cs"
];
result.Valid.Should().BeTrue();
result.Files.Should().HaveCount(fileNames.Length + 1);
foreach (var fileName in fileNames.Select((fileName, index) => new { fileName, index }))
{
var builder = result.Files[fileName.index + 1]; // +1 means skip the attribute
File.WriteAllText($"{OutputPath}{fileName.fileName}", builder.Text);
}
}
[Fact]
public void GenerateFiles_InheritedInterface_Should_Not_InheritExplicitImplementedInterfaces()
{
var name = "Explicit";
var interfaceName = "I" + name;
var proxyName = name + "Proxy";
// Arrange
string[] fileNames = [$"{Namespace}.{interfaceName}.g.cs", $"{Namespace}.{proxyName}.g.cs"];
var path = $"./Source/Disposable/{interfaceName}.cs";
SourceFile sourceFile = CreateSourceFile(
path,
name,
ImplementationOptions.UseExtendedInterfaces
);
// Act
var result = _sut.Execute([sourceFile]);
result.Valid.Should().BeTrue();
result.Files.Should().HaveCount(fileNames.Length + 1);
WriteFiles(fileNames, result);
var interfaceIndex = 0;
var tree = result.Files[interfaceIndex].SyntaxTree;
var root = tree.GetRoot();
var interfaceDeclarations = root.DescendantNodes().OfType<InterfaceDeclarationSyntax>();
// Assert
//This actually could work, we just need to implenent the logic inside the Proxy (and interface).
//⚠ Dispose is not a public member of the 'Explicit' class and also not of the Proxy.
//e.g. new Explicit().Dipose() is not possible.
Assert.Single(interfaceDeclarations);
var baseList = interfaceDeclarations.First().BaseList;
bool noInterfaceImplementationFound = baseList is null;
Assert.True(noInterfaceImplementationFound);
}
private static SourceFile CreateSourceFile(
string path,
string name,
ImplementationOptions options
)
{
var o = string.Empty;
foreach (var val in Enum.GetValues<ImplementationOptions>())
{
if (val == ImplementationOptions.None || !options.HasFlag(val))
{
continue;
}
if (o.Length > 0)
{
o += " | ";
}
o += "ImplementationOptions." + val;
}
if (o.Length == 0)
{
o = "ImplementationOptions.None";
}
return new SourceFile
{
Path = path,
Text = File.ReadAllText(path),
AttributeToAddToInterface = new ExtraAttribute
{
Name = "Speckle.ProxyGenerator.Proxy",
ArgumentList = $"typeof({Namespace}.{name}), {o}"
}
};
}
private static void WriteFiles(string[] fileNames, ExecuteResult result)
{
foreach (var fileName in fileNames.Select((fileName, index) => new { fileName, index }))
{
var builder = result.Files[fileName.index]; // attribute is always last
builder.Path.Should().EndWith(fileName.fileName);
File.WriteAllText($"{OutputPath}{fileName.fileName}", builder.Text);
builder.Text.Should().Be(File.ReadAllText($"{OutputPath}{fileName.fileName}"));
}
}
}