Closes #4
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
using System.Reflection;
|
||||
using FinalFactory.Rendezvous.Contracts;
|
||||
|
||||
namespace FinalFactory.Rendezvous.Tests.Contracts;
|
||||
|
||||
public sealed class PublicApiCompatibilityTests
|
||||
{
|
||||
[Fact]
|
||||
public void ContractsPublicApiMatchesTheV1Snapshot()
|
||||
{
|
||||
string snapshot = CreateSnapshot(typeof(ContractLimits).Assembly);
|
||||
string expected = ContractTestFiles.Read("contracts-public-api.txt");
|
||||
if (expected == "SNAPSHOT_PENDING"
|
||||
&& Environment.GetEnvironmentVariable("RENDEZVOUS_UPDATE_CONTRACT_SNAPSHOT") == "1")
|
||||
{
|
||||
string snapshotPath = Path.Combine(
|
||||
ContractTestFiles.Directory,
|
||||
"contracts-public-api.txt");
|
||||
File.WriteAllText(snapshotPath, snapshot + Environment.NewLine);
|
||||
expected = snapshot;
|
||||
}
|
||||
|
||||
Assert.Equal(expected, snapshot);
|
||||
}
|
||||
|
||||
private static string CreateSnapshot(Assembly assembly)
|
||||
{
|
||||
List<string> lines = [];
|
||||
foreach (Type type in assembly.GetExportedTypes().OrderBy(static type => type.FullName, StringComparer.Ordinal))
|
||||
{
|
||||
lines.Add($"TYPE {FormatType(type)}");
|
||||
if (type.IsEnum)
|
||||
{
|
||||
foreach (string name in Enum.GetNames(type))
|
||||
{
|
||||
object value = Enum.Parse(type, name);
|
||||
lines.Add($" ENUM {name}={Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture)}");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.OrderBy(static field => field.Name, StringComparer.Ordinal))
|
||||
{
|
||||
string value = field.IsLiteral
|
||||
? Convert.ToString(field.GetRawConstantValue(), System.Globalization.CultureInfo.InvariantCulture) ?? "null"
|
||||
: "non-literal";
|
||||
lines.Add($" FIELD {FormatType(field.FieldType)} {field.Name}={value}");
|
||||
}
|
||||
|
||||
foreach (ConstructorInfo constructor in type.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
|
||||
.OrderBy(static constructor => FormatParameters(constructor.GetParameters()), StringComparer.Ordinal))
|
||||
{
|
||||
lines.Add($" CTOR ({FormatParameters(constructor.GetParameters())})");
|
||||
}
|
||||
|
||||
foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.OrderBy(static property => property.Name, StringComparer.Ordinal))
|
||||
{
|
||||
string accessors = $"{(property.CanRead ? "get;" : string.Empty)}{(property.CanWrite ? "set;" : string.Empty)}";
|
||||
lines.Add($" PROP {FormatType(property.PropertyType)} {property.Name} {{{accessors}}}");
|
||||
}
|
||||
|
||||
foreach (MethodInfo method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
||||
.Where(static method => !method.IsSpecialName || method.Name.StartsWith("op_", StringComparison.Ordinal))
|
||||
.OrderBy(static method => method.Name, StringComparer.Ordinal)
|
||||
.ThenBy(static method => FormatParameters(method.GetParameters()), StringComparer.Ordinal))
|
||||
{
|
||||
lines.Add($" METHOD {FormatType(method.ReturnType)} {method.Name}({FormatParameters(method.GetParameters())})");
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join('\n', lines);
|
||||
}
|
||||
|
||||
private static string FormatParameters(ParameterInfo[] parameters) => string.Join(
|
||||
", ",
|
||||
parameters.Select(static parameter =>
|
||||
$"{FormatType(parameter.ParameterType)} {parameter.Name}"));
|
||||
|
||||
private static string FormatType(Type type)
|
||||
{
|
||||
if (type.IsByRef)
|
||||
{
|
||||
return $"{FormatType(type.GetElementType()!)}&";
|
||||
}
|
||||
|
||||
if (type.IsArray)
|
||||
{
|
||||
return $"{FormatType(type.GetElementType()!)}[]";
|
||||
}
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
string name = type.GetGenericTypeDefinition().FullName!;
|
||||
name = name[..name.IndexOf('`')];
|
||||
return $"{name}<{string.Join(",", type.GetGenericArguments().Select(FormatType))}>";
|
||||
}
|
||||
|
||||
return type.FullName ?? type.Name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user