// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using Xunit; namespace Microsoft.Extensions.Internal { public class ClosedGenericMatcherTest { // queryType, interfaceType, expectedResult public static TheoryData ExtractGenericInterfaceDataSet { get { return new TheoryData { // Closed generic types that match given open generic type. { typeof(IEnumerable), typeof(IEnumerable<>), typeof(IEnumerable) }, { typeof(IReadOnlyList), typeof(IReadOnlyList<>), typeof(IReadOnlyList) }, { typeof(KeyValuePair), typeof(KeyValuePair<,>), typeof(KeyValuePair) }, // Closed generic interfaces that implement sub-interface of given open generic type. { typeof(ICollection), typeof(IEnumerable<>), typeof(IEnumerable) }, { typeof(IReadOnlyList), typeof(IEnumerable<>), typeof(IEnumerable) }, { typeof(IDictionary), typeof(IEnumerable<>), typeof(IEnumerable>) }, // Class that implements closed generic based on given open generic interface. { typeof(BaseClass), typeof(IDictionary<,>), typeof(IDictionary) }, { typeof(BaseClass), typeof(IEquatable<>), typeof(IEquatable) }, { typeof(BaseClass), typeof(ICollection<>), typeof(ICollection>) }, // Derived class that implements closed generic based on given open generic interface. { typeof(DerivedClass), typeof(IDictionary<,>), typeof(IDictionary) }, { typeof(DerivedClass), typeof(IEquatable<>), typeof(IEquatable) }, { typeof(DerivedClass), typeof(ICollection<>), typeof(ICollection>) }, // Derived class that also implements another interface. { typeof(DerivedClassWithComparable), typeof(IDictionary<,>), typeof(IDictionary) }, { typeof(DerivedClassWithComparable), typeof(IEquatable<>), typeof(IEquatable) }, { typeof(DerivedClassWithComparable), typeof(ICollection<>), typeof(ICollection>) }, { typeof(DerivedClassWithComparable), typeof(IComparable<>), typeof(IComparable) }, // Derived class using system implementation. { typeof(DerivedClassFromSystemImplementation), typeof(ICollection<>), typeof(ICollection) }, { typeof(DerivedClassFromSystemImplementation), typeof(IReadOnlyList<>), typeof(IReadOnlyList) }, { typeof(DerivedClassFromSystemImplementation), typeof(IEnumerable<>), typeof(IEnumerable) }, // Not given an open generic type. { typeof(IEnumerable), typeof(IEnumerable), null }, { typeof(IEnumerable), typeof(IEnumerable), null }, { typeof(IReadOnlyList), typeof(BaseClass), null }, { typeof(KeyValuePair<,>), typeof(KeyValuePair), null }, // Not a match. { typeof(IEnumerable), typeof(IReadOnlyList<>), null }, { typeof(IList), typeof(IReadOnlyList<>), null }, { typeof(IDictionary), typeof(KeyValuePair<,>), null }, }; } } [Theory] [MemberData(nameof(ExtractGenericInterfaceDataSet))] public void ExtractGenericInterface_ReturnsExpectedType( Type queryType, Type interfaceType, Type expectedResult) { // Arrange & Act var result = ClosedGenericMatcher.ExtractGenericInterface(queryType, interfaceType); // Assert Assert.Equal(expectedResult, result); } // IEnumerable is preferred because it is defined on the more-derived type. [Fact] public void ExtractGenericInterface_MultipleDefinitionsInherited() { // Arrange var type = typeof(TwoIEnumerableImplementationsInherited); // Act var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); // Sort Assert.Equal(typeof(IEnumerable), result); } // IEnumerable is preferred because we sort by Ordinal on the full name. [Fact] public void ExtractGenericInterface_MultipleDefinitionsOnSameType() { // Arrange var type = typeof(TwoIEnumerableImplementationsOnSameClass); // Act var result = ClosedGenericMatcher.ExtractGenericInterface(type, typeof(IEnumerable<>)); // Sort Assert.Equal(typeof(IEnumerable), result); } private class TwoIEnumerableImplementationsOnSameClass : IEnumerable, IEnumerable { IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } private class TwoIEnumerableImplementationsInherited : List, IEnumerable { IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } } private class BaseClass : IDictionary, IEquatable { object IDictionary.this[string key] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } int ICollection>.Count { get { throw new NotImplementedException(); } } bool ICollection>.IsReadOnly { get { throw new NotImplementedException(); } } ICollection IDictionary.Keys { get { throw new NotImplementedException(); } } ICollection IDictionary.Values { get { throw new NotImplementedException(); } } public bool Equals(BaseClass other) { throw new NotImplementedException(); } void ICollection>.Add(KeyValuePair item) { throw new NotImplementedException(); } void IDictionary.Add(string key, object value) { throw new NotImplementedException(); } void ICollection>.Clear() { throw new NotImplementedException(); } bool ICollection>.Contains(KeyValuePair item) { throw new NotImplementedException(); } bool IDictionary.ContainsKey(string key) { throw new NotImplementedException(); } void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { throw new NotImplementedException(); } IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } IEnumerator> IEnumerable>.GetEnumerator() { throw new NotImplementedException(); } bool ICollection>.Remove(KeyValuePair item) { throw new NotImplementedException(); } bool IDictionary.Remove(string key) { throw new NotImplementedException(); } bool IDictionary.TryGetValue(string key, out object value) { throw new NotImplementedException(); } } private class DerivedClass : BaseClass { } private class DerivedClassWithComparable : DerivedClass, IComparable { public int CompareTo(DerivedClassWithComparable other) { throw new NotImplementedException(); } } private class DerivedClassFromSystemImplementation : Collection { } } }