129 lines
5.4 KiB
C#
129 lines
5.4 KiB
C#
using System.Reflection;
|
|
using FinalFactory.Rendezvous.Client;
|
|
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);
|
|
}
|
|
|
|
[Fact]
|
|
public void ClientPublicApiMatchesTheV1Snapshot()
|
|
{
|
|
string snapshot = CreateSnapshot(typeof(RendezvousPublisherClient).Assembly);
|
|
string expected = ContractTestFiles.Read("client-public-api.txt");
|
|
if (expected == "SNAPSHOT_PENDING"
|
|
&& Environment.GetEnvironmentVariable("RENDEZVOUS_UPDATE_CONTRACT_SNAPSHOT") == "1")
|
|
{
|
|
string snapshotPath = Path.Combine(
|
|
ContractTestFiles.Directory,
|
|
"client-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.GetMethod?.IsPublic == true ? "get;" : string.Empty)}{(property.SetMethod?.IsPublic == true ? "set;" : string.Empty)}";
|
|
lines.Add($" PROP {FormatType(property.PropertyType)} {property.Name} {{{accessors}}}");
|
|
}
|
|
|
|
foreach (EventInfo eventInfo in type.GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
|
|
.OrderBy(static eventInfo => eventInfo.Name, StringComparer.Ordinal))
|
|
{
|
|
lines.Add($" EVENT {FormatType(eventInfo.EventHandlerType!)} {eventInfo.Name}");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|