From d2aff11d5c533ea3ff4f5f025489f6ecb23bf311 Mon Sep 17 00:00:00 2001 From: lbargaoanu Date: Fri, 7 Feb 2014 10:50:36 +0200 Subject: [PATCH] check for DebuggerDisplay on models --- .../DebuggerDisplayOnModels.cs | 38 ++++++ .../Octokit.Tests.Conventions.csproj | 6 + .../SyncObservableClients.cs | 116 +++++++----------- Octokit.Tests.Conventions/TypeExtensions.cs | 97 +++++++++++++++ Octokit.Tests/Helpers/AssertEx.cs | 6 + 5 files changed, 188 insertions(+), 75 deletions(-) create mode 100644 Octokit.Tests.Conventions/DebuggerDisplayOnModels.cs create mode 100644 Octokit.Tests.Conventions/TypeExtensions.cs diff --git a/Octokit.Tests.Conventions/DebuggerDisplayOnModels.cs b/Octokit.Tests.Conventions/DebuggerDisplayOnModels.cs new file mode 100644 index 00000000..527772ef --- /dev/null +++ b/Octokit.Tests.Conventions/DebuggerDisplayOnModels.cs @@ -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(modelType); + } + } + } +} \ No newline at end of file diff --git a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj index e0f096f5..7b378297 100644 --- a/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj +++ b/Octokit.Tests.Conventions/Octokit.Tests.Conventions.csproj @@ -49,8 +49,10 @@ + + @@ -60,6 +62,10 @@ {674b69b8-0780-4d54-ae2b-c15821fa51cb} Octokit.Reactive + + {149448d4-c2f2-4df9-86bd-03e3272f093b} + Octokit.Tests + {08dd4305-7787-4823-a53f-4d0f725a07f3} Octokit diff --git a/Octokit.Tests.Conventions/SyncObservableClients.cs b/Octokit.Tests.Conventions/SyncObservableClients.cs index d29cdf86..b1f60458 100644 --- a/Octokit.Tests.Conventions/SyncObservableClients.cs +++ b/Octokit.Tests.Conventions/SyncObservableClients.cs @@ -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 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 - if(!mainReturnType.IsGenericType) - { - Assert.Equal(typeof(IObservable), observableReturnType); - return; - } - var taskResultType = mainReturnType.GetGenericArgument(); - // single item - Task => IObservable - // list - Task> => IObservable - 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 + return typeof(IObservable); + case TypeCategory.GenericTask: + // single item - Task => IObservable + case TypeCategory.ReadOnlyList: + // list - Task> => IObservable + 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 { - const string ClientSufix = "Client"; - const string ObservablePrefix = "IObservable"; - const int RealNameIndex = 1; + private readonly IEnumerable data = GetClientInterfaces(); - public static ParameterInfo[] GetParametersOrdered(this MethodInfo method) + public static IEnumerable 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 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(); } } } \ No newline at end of file diff --git a/Octokit.Tests.Conventions/TypeExtensions.cs b/Octokit.Tests.Conventions/TypeExtensions.cs new file mode 100644 index 00000000..211be051 --- /dev/null +++ b/Octokit.Tests.Conventions/TypeExtensions.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Octokit.Tests/Helpers/AssertEx.cs b/Octokit.Tests/Helpers/AssertEx.cs index cbb8d888..24265d17 100644 --- a/Octokit.Tests/Helpers/AssertEx.cs +++ b/Octokit.Tests/Helpers/AssertEx.cs @@ -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(MemberInfo memberInfo, bool inherit = false) where TAttribute : Attribute + { + Assert.True(memberInfo.IsDefined(typeof(TAttribute), inherit), memberInfo.ToString() + Environment.NewLine); + } + public async static Task Throws(Func testCode) where T : Exception { try