[XamlC] Type ref tests, and fixes (#569)
authorStephane Delcroix <stephane@delcroix.org>
Wed, 7 Dec 2016 09:57:43 +0000 (10:57 +0100)
committerGitHub <noreply@github.com>
Wed, 7 Dec 2016 09:57:43 +0000 (10:57 +0100)
* [XamlC] Add tests for TypeRefExts, fix and enhancements

* [XamlC] more fixes, more tests

* Fix failing test

Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs
Xamarin.Forms.Xaml.UnitTests/Xamarin.Forms.Xaml.UnitTests.csproj
Xamarin.Forms.Xaml.UnitTests/XamlC/TypeReferenceExtensionsTests.cs [new file with mode: 0644]

index 613b0cc..9e1cc16 100644 (file)
@@ -6,6 +6,33 @@ using Mono.Cecil.Rocks;
 
 namespace Xamarin.Forms.Build.Tasks
 {
+       class TypeRefComparer : IEqualityComparer<TypeReference>
+       {
+               static string GetAssembly(TypeReference typeRef)
+               {
+                       var md = typeRef.Scope as ModuleDefinition;
+                       if (md != null)
+                               return md.Assembly.FullName;
+                       var anr = typeRef.Scope as AssemblyNameReference;
+                       if (anr != null)
+                               return anr.FullName;
+                       throw new ArgumentOutOfRangeException(nameof(typeRef));
+               }
+
+               public bool Equals(TypeReference x, TypeReference y)
+               {
+                       return GetAssembly(x) == GetAssembly(y) && x.FullName == y.FullName;
+               }
+
+               public int GetHashCode(TypeReference obj)
+               {
+                       return $"{GetAssembly(obj)}//{obj.FullName}".GetHashCode();
+               }
+
+               static TypeRefComparer s_default;
+               public static TypeRefComparer Default => s_default ?? (s_default = new TypeRefComparer());
+       }
+
        static class TypeReferenceExtensions
        {
                public static PropertyDefinition GetProperty(this TypeReference typeRef, Func<PropertyDefinition, bool> predicate,
@@ -79,54 +106,56 @@ namespace Xamarin.Forms.Build.Tasks
                        return false;
                }
 
+               static readonly string[] arrayInterfaces = {
+                       "System.ICloneable",
+                       "System.Collections.IEnumerable",
+                       "System.Collections.IList",
+                       "System.Collections.ICollection",
+                       "System.Collections.IStructuralComparable",
+                       "System.Collections.IStructuralEquatable",
+               };
+
+               static readonly string[] arrayGenericInterfaces = {
+                       "System.Collections.Generic.IEnumerable`1",
+                       "System.Collections.Generic.IList`1",
+                       "System.Collections.Generic.ICollection`1",
+                       "System.Collections.Generic.IReadOnlyCollection`1",
+                       "System.Collections.Generic.IReadOnlyList`1",
+               };
+
                public static bool InheritsFromOrImplements(this TypeReference typeRef, TypeReference baseClass)
                {
-                       if (typeRef.FullName == baseClass.FullName)
+                       if (TypeRefComparer.Default.Equals(typeRef, baseClass))
                                return true;
 
-                       var arrayInterfaces = new[]
-                       {
-                               "System.Collections.IEnumerable",
-                               "System.Collections.IList",
-                               "System.Collections.Collection"
-                       };
-
-                       var arrayGenericInterfaces = new[]
-                       {
-                               "System.Collections.IEnumerable`1",
-                               "System.Collections.Generic.IList`1",
-                               "System.Collections.Generic.IReadOnlyCollection<T>",
-                               "System.Collections.Generic.IReadOnlyList<T>",
-                               "System.Collections.Generic.Collection<T>"
-                       };
-
-                       if (typeRef.IsArray && baseClass.IsArray) {
-                               typeRef = typeRef.Resolve();
-                               baseClass = baseClass.Resolve();
-                       }
+                       if (typeRef.IsValueType)
+                               return false;
 
-                       if (typeRef.IsArray)
-                       {
+                       if (typeRef.IsArray) {
+                               var array = (ArrayType)typeRef;
                                var arrayType = typeRef.Resolve();
                                if (arrayInterfaces.Contains(baseClass.FullName))
                                        return true;
-                               if (arrayGenericInterfaces.Contains(baseClass.Resolve().FullName) &&
-                                   baseClass.IsGenericInstance &&
-                                   (baseClass as GenericInstanceType).GenericArguments[0].FullName == arrayType.FullName)
+                               if (array.IsVector &&  //generic interfaces are not implemented on multidimensional arrays
+                                       arrayGenericInterfaces.Contains(baseClass.Resolve().FullName) &&
+                                       baseClass.IsGenericInstance &&
+                                       TypeRefComparer.Default.Equals((baseClass as GenericInstanceType).GenericArguments[0], arrayType))
                                        return true;
-                               return false;
+                               return baseClass.FullName == "System.Object";
                        }
 
+                       if (typeRef.FullName == "System.Object")
+                               return false;
                        var typeDef = typeRef.Resolve();
-                       if (typeDef.FullName == baseClass.FullName)
+                       if (TypeRefComparer.Default.Equals(typeDef, baseClass.Resolve()))
                                return true;
-                       if (typeDef.Interfaces.Any(ir => ir.FullName == baseClass.FullName))
+                       if (typeDef.Interfaces.Any(ir => TypeRefComparer.Default.Equals(ir, baseClass)))
                                return true;
-                       if (typeDef.FullName == "System.Object")
-                               return false;
                        if (typeDef.BaseType == null)
                                return false;
-                       return typeDef.BaseType.InheritsFromOrImplements(baseClass);
+
+                       typeRef = typeDef.BaseType.ResolveGenericParameters(typeRef);
+                       return typeRef.InheritsFromOrImplements(baseClass);
                }
 
                public static CustomAttribute GetCustomAttribute(this TypeReference typeRef, TypeReference attribute)
@@ -248,4 +277,4 @@ namespace Xamarin.Forms.Build.Tasks
                        return self.GetElementType().MakeGenericInstanceType(args.ToArray());
                }
        }
-}
\ No newline at end of file
+}
index 4977edb..4f56c57 100644 (file)
      <Compile Include="FactoryMethodMissingMethod.xaml.cs">
        <DependentUpon>FactoryMethodMissingMethod.xaml</DependentUpon>
      </Compile>
+    <Compile Include="XamlC\TypeReferenceExtensionsTests.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="..\.nuspec\Xamarin.Forms.Debug.targets" />
diff --git a/Xamarin.Forms.Xaml.UnitTests/XamlC/TypeReferenceExtensionsTests.cs b/Xamarin.Forms.Xaml.UnitTests/XamlC/TypeReferenceExtensionsTests.cs
new file mode 100644 (file)
index 0000000..3b6f293
--- /dev/null
@@ -0,0 +1,112 @@
+using System;
+using NUnit.Framework;
+using Mono.Cecil;
+using Xamarin.Forms.Build.Tasks;
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+       public class Effect
+       {
+       }
+}
+namespace Xamarin.Forms.Xaml.XamlcUnitTests
+{
+       [TestFixture]
+       public class TypeReferenceExtensionsTests
+       {
+               class Foo
+               {
+               }
+
+               class Foo<T> : Foo
+               {
+               }
+
+               class Bar<T> : Foo<T>
+               {
+               }
+
+               ModuleDefinition module;
+
+               [SetUp]
+               public void SetUp()
+               {
+                       var resolver = new XamlCAssemblyResolver();
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(TypeReferenceExtensionsTests).Assembly.CodeBase)).Path));
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(BindableObject).Assembly.CodeBase)).Path));
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(object).Assembly.CodeBase)).Path));
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(IList<>).Assembly.CodeBase)).Path));
+                       resolver.AddAssembly(Uri.UnescapeDataString((new UriBuilder(typeof(Queue<>).Assembly.CodeBase)).Path));
+
+                       module = ModuleDefinition.CreateModule("foo", new ModuleParameters {
+                               AssemblyResolver = resolver,
+                               Kind = ModuleKind.NetModule
+                       });
+               }
+
+               [TestCase(typeof(bool), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(Dictionary<string, string>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(List<string>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(List<Button>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(Queue<KeyValuePair<string, string>>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(double), typeof(double), ExpectedResult = true)]
+               [TestCase(typeof(object), typeof(IList<TriggerBase>), ExpectedResult = false)]
+               [TestCase(typeof(object), typeof(double), ExpectedResult = false)]
+               [TestCase(typeof(object), typeof(int?), ExpectedResult = false)]
+               [TestCase(typeof(object), typeof(object), ExpectedResult = true)]
+               [TestCase(typeof(sbyte), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(string[]), typeof(System.Collections.IEnumerable), ExpectedResult = true)]
+               [TestCase(typeof(string[]), typeof(object), ExpectedResult = true)]
+               [TestCase(typeof(string[]), typeof(string), ExpectedResult = false)]
+               [TestCase(typeof(string[]), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(string[]), typeof(IEnumerable<string>), ExpectedResult = true)]
+               [TestCase(typeof(Type), typeof(object), ExpectedResult = true)]
+               [TestCase(typeof(Type), typeof(Type), ExpectedResult = true)]
+               [TestCase(typeof(Type), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(System.Windows.Input.ICommand), typeof(System.Windows.Input.ICommand), ExpectedResult = true)]
+               [TestCase(typeof(System.Windows.Input.ICommand), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(BindingBase), typeof(BindingBase), ExpectedResult = true)]
+               [TestCase(typeof(BindingCondition), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(Button), typeof(BindableObject), ExpectedResult = true)]
+               [TestCase(typeof(Button), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(Button), typeof(View), ExpectedResult = true)]
+               [TestCase(typeof(Color), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(Color), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(Color), typeof(Color), ExpectedResult = true)]
+               [TestCase(typeof(ColumnDefinition), typeof(BindableObject), ExpectedResult = true)]
+               [TestCase(typeof(ColumnDefinition), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(ColumnDefinition), typeof(ColumnDefinitionCollection), ExpectedResult = false)]
+               [TestCase(typeof(Constraint), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(Constraint), typeof(Constraint), ExpectedResult = true)]
+               [TestCase(typeof(ConstraintExpression), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(ContentPage), typeof(BindableObject), ExpectedResult = true)]
+               [TestCase(typeof(ContentPage), typeof(Page), ExpectedResult = true)]
+               [TestCase(typeof(ContentView), typeof(BindableObject), ExpectedResult = true)]
+               [TestCase(typeof(ContentView[]), typeof(IList<ContentView>), ExpectedResult = true)]
+               [TestCase(typeof(MultiTrigger), typeof(IList<TriggerBase>), ExpectedResult = false)]
+               [TestCase(typeof(OnIdiom<double>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(OnPlatform<string>), typeof(string), ExpectedResult = false)]
+               [TestCase(typeof(OnPlatform<string>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(OnPlatform<string>), typeof(BindingBase), ExpectedResult = false)]
+               [TestCase(typeof(OnPlatform<FontAttributes>), typeof(BindableObject), ExpectedResult = false)]
+               [TestCase(typeof(StackLayout), typeof(Layout<View>), ExpectedResult = true)]
+               [TestCase(typeof(StackLayout), typeof(View), ExpectedResult = true)]
+               [TestCase(typeof(Foo<string>), typeof(Foo), ExpectedResult = true)]
+               [TestCase(typeof(Bar<string>), typeof(Foo), ExpectedResult = true)]
+               [TestCase(typeof(Bar<string>), typeof(Foo<string>), ExpectedResult = true)]
+               public bool TestInheritsFromOrImplements(Type typeRef, Type baseClass)
+               {
+                       return TypeReferenceExtensions.InheritsFromOrImplements(module.Import(typeRef), module.Import(baseClass));
+               }
+
+               [Test]
+               public void TestSameTypeNamesFromDifferentAssemblies()
+               {
+                       var core = typeof(BindableObject).Assembly;
+                       var test = typeof(TypeReferenceExtensionsTests).Assembly;
+
+                       Assert.False(TestInheritsFromOrImplements(test.GetType("Xamarin.Forms.Effect"), core.GetType("Xamarin.Forms.Effect")));
+               }
+       }
+}
\ No newline at end of file