check for DebuggerDisplay on models

This commit is contained in:
lbargaoanu
2014-02-07 10:50:36 +02:00
parent 0c389f26a5
commit d2aff11d5c
5 changed files with 188 additions and 75 deletions
@@ -0,0 +1,38 @@
using System;
using System.Diagnostics;
using System.Linq;
using Octokit.Tests.Helpers;
using Xunit;
using Xunit.Extensions;
namespace Octokit.Tests.Conventions
{
public class DebuggerDisplayOnModels
{
[Fact]
public void CheckModelsForDebuggerDisplayAttributeExample()
{
CheckModelsForDebuggerDisplayAttribute(typeof(IAuthorizationsClient));
}
[Theory]
[ClassData(typeof(ClientInterfaces))]
public void CheckModelsForDebuggerDisplayAttribute(Type clientInterface)
{
var methods = clientInterface.GetMethods();
var modelTypes =
from modelType in
(from type in (
from method in methods from parameter in method.GetParameters() select parameter.ParameterType
).Union(
from method in methods select method.ReturnType)
select type.GetTypeInfo().Type)
where TypeExtensions.IsModel(modelType)
select modelType;
foreach(var modelType in modelTypes.Distinct())
{
AssertEx.HasAttribute<DebuggerDisplayAttribute>(modelType);
}
}
}
}
@@ -49,8 +49,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="DebuggerDisplayOnModels.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SyncObservableClients.cs" />
<Compile Include="TypeExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -60,6 +62,10 @@
<Project>{674b69b8-0780-4d54-ae2b-c15821fa51cb}</Project>
<Name>Octokit.Reactive</Name>
</ProjectReference>
<ProjectReference Include="..\Octokit.Tests\Octokit.Tests.csproj">
<Project>{149448d4-c2f2-4df9-86bd-03e3272f093b}</Project>
<Name>Octokit.Tests</Name>
</ProjectReference>
<ProjectReference Include="..\Octokit\Octokit.csproj">
<Project>{08dd4305-7787-4823-a53f-4d0f725a07f3}</Project>
<Name>Octokit</Name>
@@ -1,10 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reflection;
using System.Threading.Tasks;
using Octokit.Reactive;
using Xunit;
using Xunit.Extensions;
@@ -12,23 +11,15 @@ namespace Octokit.Tests.Conventions
{
public class SyncObservableClients
{
public static IEnumerable<object[]> ClientInterfaces
{
get
{
return typeof(IEventsClient).Assembly.ExportedTypes.Where(TypeExtensions.IsClientInterface).Select(type => new[]{type});
}
}
[Fact]
private void CheckClientExample()
private void CheckObservableClientExample()
{
CheckClientInterfaces(typeof(IAssigneesClient));
CheckObservableClients(typeof(IAssigneesClient));
}
[Theory]
[PropertyData("ClientInterfaces")]
private void CheckClientInterfaces(Type clientInterface)
[ClassData(typeof(ClientInterfaces))]
private void CheckObservableClients(Type clientInterface)
{
var observableClient = clientInterface.GetObservableClientInterface();
var mainMethods = clientInterface.GetMethodsOrdered();
@@ -55,37 +46,33 @@ namespace Octokit.Tests.Conventions
{
var mainReturnType = mainMethod.ReturnType;
var observableReturnType = observableMethod.ReturnType;
if(mainReturnType.IsTask())
{
CheckTaskReturnType(mainReturnType, observableReturnType);
return;
}
CheckClientInterface(mainReturnType, observableReturnType);
}
private static void CheckClientInterface(Type mainType, Type observableType)
{
// client interface - IClient => IObservableClient
var expectedType = mainType.IsClientInterface() ? mainType.GetObservableClientInterface() : mainType;
Assert.Equal(expectedType, observableType);
}
private static void CheckTaskReturnType(Type mainReturnType, Type observableReturnType)
{
// void - Task => IObservable<Unit>
if(!mainReturnType.IsGenericType)
{
Assert.Equal(typeof(IObservable<Unit>), observableReturnType);
return;
}
var taskResultType = mainReturnType.GetGenericArgument();
// single item - Task<TResult> => IObservable<TResult>
// list - Task<IReadOnlyList<TResult>> => IObservable<TResult>
var expectedInnerType = taskResultType.IsList() ? taskResultType.GetGenericArgument() : taskResultType;
var expectedType = typeof(IObservable<>).MakeGenericType(expectedInnerType);
var expectedType = GetObservableExpectedType(mainReturnType);
Assert.Equal(expectedType, observableReturnType);
}
private static Type GetObservableExpectedType(Type mainType)
{
var typeInfo = mainType.GetTypeInfo();
switch(typeInfo.TypeCategory)
{
case TypeCategory.ClientInterface:
// client interface - IClient => IObservableClient
return mainType.GetObservableClientInterface();
case TypeCategory.Task:
// void - Task => IObservable<Unit>
return typeof(IObservable<Unit>);
case TypeCategory.GenericTask:
// single item - Task<TResult> => IObservable<TResult>
case TypeCategory.ReadOnlyList:
// list - Task<IReadOnlyList<TResult>> => IObservable<TResult>
return typeof(IObservable<>).MakeGenericType(typeInfo.Type);
case TypeCategory.Other:
return mainType;
default:
throw new Exception("Unknown type category " + typeInfo.TypeCategory);
}
}
private static void CheckParameters(MethodInfo mainMethod, MethodInfo observableMethod)
{
var mainParameters = mainMethod.GetParametersOrdered();
@@ -96,53 +83,32 @@ namespace Octokit.Tests.Conventions
{
var observableParameter = observableParameters[index];
Assert.Equal(mainParameter.Name, observableParameter.Name);
CheckClientInterface(mainParameter.ParameterType, observableParameter.ParameterType);
var mainType = mainParameter.ParameterType;
var typeInfo = mainType.GetTypeInfo();
var expectedType = GetObservableExpectedType(mainType);
Assert.Equal(expectedType, observableParameter.ParameterType);
index++;
}
}
}
public static class TypeExtensions
public class ClientInterfaces : IEnumerable<object[]>
{
const string ClientSufix = "Client";
const string ObservablePrefix = "IObservable";
const int RealNameIndex = 1;
private readonly IEnumerable<object[]> data = GetClientInterfaces();
public static ParameterInfo[] GetParametersOrdered(this MethodInfo method)
public static IEnumerable<object[]> GetClientInterfaces()
{
return method.GetParameters().OrderBy(p=>p.Name).ToArray();
return typeof(IEventsClient).Assembly.ExportedTypes.Where(TypeExtensions.IsClientInterface).Select(type => new[] { type });
}
public static MethodInfo[] GetMethodsOrdered(this Type type)
public IEnumerator<object[]> GetEnumerator()
{
return type.GetMethods().OrderBy(m=>m.Name).ToArray();
return data.GetEnumerator();
}
public static bool IsClientInterface(this Type type)
IEnumerator IEnumerable.GetEnumerator()
{
return type.IsInterface && type.Name.EndsWith(ClientSufix) && type.Namespace == typeof(IEventsClient).Namespace;
}
public static Type GetObservableClientInterface(this Type type)
{
var observableClient = typeof(IObservableEventsClient);
var observableClientName = observableClient.Namespace + "." + ObservablePrefix + type.Name.Substring(RealNameIndex);
return observableClient.Assembly.GetType(observableClientName, throwOnError: true);
}
public static bool IsTask(this Type type)
{
return typeof(Task).IsAssignableFrom(type);
}
public static bool IsList(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IReadOnlyList<>);
}
public static Type GetGenericArgument(this Type type)
{
return type.GetGenericArguments()[0];
return GetEnumerator();
}
}
}
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Octokit.Reactive;
namespace Octokit.Tests.Conventions
{
public static class TypeExtensions
{
const string ClientSufix = "Client";
const string ObservablePrefix = "IObservable";
const int RealNameIndex = 1;
public static ParameterInfo[] GetParametersOrdered(this MethodInfo method)
{
return method.GetParameters().OrderBy(p => p.Name).ToArray();
}
public static MethodInfo[] GetMethodsOrdered(this Type type)
{
return type.GetMethods().OrderBy(m => m.Name).ToArray();
}
public static TypeInfo GetTypeInfo(this Type type)
{
var typeInfo = new TypeInfo { Type = type, TypeCategory = TypeCategory.Other };
if(type.IsClientInterface())
{
typeInfo.TypeCategory = TypeCategory.ClientInterface;
}
else if(type.IsTask())
{
if(!type.IsGenericType)
{
typeInfo.TypeCategory = TypeCategory.Task;
}
else
{
var taskResultType = type.GetGenericArgument();
if(taskResultType.IsList())
{
typeInfo.TypeCategory = TypeCategory.ReadOnlyList;
typeInfo.Type = taskResultType.GetGenericArgument();
}
else
{
typeInfo.TypeCategory = TypeCategory.GenericTask;
typeInfo.Type = taskResultType;
}
}
}
return typeInfo;
}
public static bool IsModel(this Type type)
{
return !type.IsInterface && type.Assembly == typeof(AuthorizationUpdate).Assembly;
}
public static bool IsClientInterface(this Type type)
{
return type.IsInterface && type.Name.EndsWith(ClientSufix) && type.Namespace == typeof(IEventsClient).Namespace;
}
public static Type GetObservableClientInterface(this Type type)
{
var observableClient = typeof(IObservableEventsClient);
var observableClientName = observableClient.Namespace + "." + ObservablePrefix + type.Name.Substring(RealNameIndex);
return observableClient.Assembly.GetType(observableClientName, throwOnError: true);
}
public static bool IsTask(this Type type)
{
return typeof(Task).IsAssignableFrom(type);
}
public static bool IsList(this Type type)
{
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IReadOnlyList<>);
}
public static Type GetGenericArgument(this Type type)
{
return type.GetGenericArguments()[0];
}
}
public enum TypeCategory { Other, Task, GenericTask, ReadOnlyList, ClientInterface }
public struct TypeInfo
{
public Type Type { get; set; }
public TypeCategory TypeCategory { get; set; }
}
}
+6
View File
@@ -1,4 +1,5 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
@@ -6,6 +7,11 @@ namespace Octokit.Tests.Helpers
{
public static class AssertEx
{
public static void HasAttribute<TAttribute>(MemberInfo memberInfo, bool inherit = false) where TAttribute : Attribute
{
Assert.True(memberInfo.IsDefined(typeof(TAttribute), inherit), memberInfo.ToString() + Environment.NewLine);
}
public async static Task<T> Throws<T>(Func<Task> testCode) where T : Exception
{
try