f5846df432c010b918904e7b48139866000fb4cf
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 using System.Collections.Generic;
6 using System.Composition.Hosting.Core;
7 using System.Globalization;
8 using System.Reflection;
9 using Xunit;
10
11 namespace System.Composition.Runtime.Tests
12 {
13     public class CompositionContractTests
14     {
15         [Theory]
16         [InlineData(typeof(int))]
17         public void Ctor_ContractType(Type contractType)
18         {
19             var contract = new CompositionContract(contractType);
20             Assert.Equal(contractType, contract.ContractType);
21             Assert.Null(contract.ContractName);
22             Assert.Null(contract.MetadataConstraints);
23         }
24
25         [Theory]
26         [InlineData(typeof(int), null)]
27         [InlineData(typeof(object), "contractName")]
28         public void Ctor_ContractType(Type contractType, string contractName)
29         {
30             var contract = new CompositionContract(contractType, contractName);
31             Assert.Equal(contractType, contract.ContractType);
32             Assert.Equal(contractName, contract.ContractName);
33             Assert.Null(contract.MetadataConstraints);
34         }
35
36         public static IEnumerable<object[]> Ctor_ContractType_ContractName_MetadataConstraints_TestData()
37         {
38             yield return new object[] { typeof(int), null, null };
39             yield return new object[] { typeof(object), "contractName", new Dictionary<string, object> { { "key", "value" } } };
40         }
41
42         [Theory]
43         [MemberData(nameof(Ctor_ContractType_ContractName_MetadataConstraints_TestData))]
44         public void Ctor_ContractType_MetadataConstraints(Type contractType, string contractName, IDictionary<string, object> metadataConstraints)
45         {
46             var contract = new CompositionContract(contractType, contractName, metadataConstraints);
47             Assert.Equal(contractType, contract.ContractType);
48             Assert.Equal(contractName, contract.ContractName);
49             Assert.Equal(metadataConstraints, contract.MetadataConstraints);
50         }
51
52         [Fact]
53         public void Ctor_NullContractType_ThrowsArgumentNullException()
54         {
55             AssertExtensions.Throws<ArgumentNullException>("contractType", () => new CompositionContract(null));
56             AssertExtensions.Throws<ArgumentNullException>("contractType", () => new CompositionContract(null, "contractName"));
57             AssertExtensions.Throws<ArgumentNullException>("contractType", () => new CompositionContract(null, "contractName", new Dictionary<string, object>()));
58         }
59
60         [Fact]
61         public void Ctor_EmptyMetadataConstraints_ThrowsArgumentOutOfRangeException()
62         {
63             AssertExtensions.Throws<ArgumentOutOfRangeException>("metadataConstraints", () => new CompositionContract(typeof(string), "contractName", new Dictionary<string, object>()));
64         }
65
66         public static IEnumerable<object[]> Equals_TestData()
67         {
68             yield return new object[] { new CompositionContract(typeof(int)), new CompositionContract(typeof(int)), true };
69             yield return new object[] { new CompositionContract(typeof(int)), new CompositionContract(typeof(string)), false };
70             yield return new object[] { new CompositionContract(typeof(int)), new CompositionContract(typeof(int), "contractName"), false };
71             yield return new object[] { new CompositionContract(typeof(int)), new CompositionContract(typeof(int), null, new Dictionary<string, object> { { "key", "value" } }), false };
72
73             yield return new object[] { new CompositionContract(typeof(int), "contractName"), new CompositionContract(typeof(int), "contractName"), true };
74             yield return new object[] { new CompositionContract(typeof(int), "contractName"), new CompositionContract(typeof(int), "ContractName"), false };
75             yield return new object[] { new CompositionContract(typeof(int), "contractName"), new CompositionContract(typeof(int)), false };
76             yield return new object[] { new CompositionContract(typeof(int)), new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }), false };
77
78             yield return new object[]
79             {
80                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
81                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
82                 true
83             };
84
85             yield return new object[]
86             {
87                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", 1 } }),
88                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", 1 } }),
89                 true
90             };
91
92             yield return new object[]
93             {
94                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[] { "1", null } } }),
95                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new object[] { "1", null } } }),
96                 true
97             };
98
99             yield return new object[]
100             {
101                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[] { "1", null } } }),
102                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new object[] { "1", new object() } } }),
103                 false
104             };
105
106             yield return new object[]
107             {
108                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
109                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" }, { "key2", "value2" } }),
110                 false
111             };
112
113             yield return new object[]
114             {
115                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" }, { "key2", "value2" }  }),
116                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
117                 false
118             };
119
120             yield return new object[]
121             {
122                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
123                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key2", "value" } }),
124                 false
125             };
126
127             yield return new object[]
128             {
129                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
130                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value2" } }),
131                 false
132             };
133
134             yield return new object[]
135             {
136                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[0] } }),
137                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[1] } }),
138                 false
139             };
140
141             yield return new object[]
142             {
143                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[0] } }),
144                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
145                 false
146             };
147
148             yield return new object[]
149             {
150                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new string[0] } }),
151                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new object() } }),
152                 false
153             };
154
155             yield return new object[]
156             {
157                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } }),
158                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
159                 false
160             };
161
162             yield return new object[]
163             {
164                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", "value" } }),
165                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } }),
166                 false
167             };
168
169             yield return new object[]
170             {
171                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", new object[0] } }),
172                 new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } }),
173                 false
174             };
175
176             if (!PlatformDetection.IsFullFramework)
177             {
178                 yield return new object[]
179                 {
180                     new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } }),
181                     new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } }),
182                     true
183                 };
184             }
185
186             yield return new object[] { new CompositionContract(typeof(int)), new object(), false };
187             yield return new object[] { new CompositionContract(typeof(int)), null, false };
188         }
189
190         [Theory]
191         [MemberData(nameof(Equals_TestData))]
192         public void Equals_Object_ReturnsExpected(CompositionContract contract, object other, bool expected)
193         {
194             Assert.Equal(expected, contract.Equals(other));
195             Assert.Equal(contract.GetHashCode(), contract.GetHashCode());
196         }
197
198         [Fact]
199         public void ChangeType_ValidType_Success()
200         {
201             var dictionary = new Dictionary<string, object> { { "key", "value" } };
202             var contract = new CompositionContract(typeof(int), "contractName", dictionary);
203             CompositionContract newContract = contract.ChangeType(typeof(string));
204
205             Assert.Equal(typeof(int), contract.ContractType);
206             Assert.Equal(typeof(string), newContract.ContractType);
207             Assert.Equal("contractName", newContract.ContractName);
208             Assert.Same(dictionary, newContract.MetadataConstraints);
209         }
210
211         [Fact]
212         public void ChangeType_NullNewContractType_ThrowsArgumentNullException()
213         {
214             var contract = new CompositionContract(typeof(int));
215             AssertExtensions.Throws<ArgumentNullException>("newContractType", () => contract.ChangeType(null));
216         }
217
218         [Fact]
219         public void TryUnwrapMetadataConstraint_NullConstraints_ReturnsFalse()
220         {
221             var contract = new CompositionContract(typeof(int));
222             Assert.False(contract.TryUnwrapMetadataConstraint("constraintName", out int constraintValue, out CompositionContract remainingContract));
223             Assert.Equal(0, constraintValue);
224             Assert.Null(remainingContract);
225         }
226
227         [Fact]
228         public void TryUnwrapMetadataConstraint_NoSuchConstraintName_ReturnsFalse()
229         {
230             var contract = new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "constraint", 1 } });
231             Assert.False(contract.TryUnwrapMetadataConstraint("constraintName", out int constraintValue, out CompositionContract remainingContract));
232             Assert.Equal(0, constraintValue);
233             Assert.Null(remainingContract);
234         }
235
236         [Fact]
237         public void TryUnwrapMetadataConstraint_IncorrectConstraintNameType_ReturnsFalse()
238         {
239             var contract = new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "constraintName", "value" } });
240             Assert.False(contract.TryUnwrapMetadataConstraint("constraintName", out int constraintValue, out CompositionContract remainingContract));
241             Assert.Equal(0, constraintValue);
242             Assert.Null(remainingContract);
243         }
244
245         [Fact]
246         public void TryUnwrapMetadataConstraint_UnwrapAllConstraints_ReturnsTrue()
247         {
248             var originalContract = new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "constraintName1", 1 }, { "constraintName2", 2 } });
249             Assert.True(originalContract.TryUnwrapMetadataConstraint("constraintName1", out int constraintValue1, out CompositionContract remainingContract1));
250             Assert.Equal(1, constraintValue1);
251
252             Assert.Equal(originalContract.ContractType, remainingContract1.ContractType);
253             Assert.Equal(originalContract.ContractName, remainingContract1.ContractName);
254             Assert.Equal(new Dictionary<string, object> { { "constraintName2", 2 } }, remainingContract1.MetadataConstraints);
255             Assert.NotEqual(originalContract.MetadataConstraints, remainingContract1.MetadataConstraints);
256
257             Assert.True(remainingContract1.TryUnwrapMetadataConstraint("constraintName2", out int constraintValue2, out CompositionContract remainingContract2));
258             Assert.Equal(2, constraintValue2);
259
260             Assert.Equal(originalContract.ContractType, remainingContract2.ContractType);
261             Assert.Equal(originalContract.ContractName, remainingContract2.ContractName);
262             Assert.Null(remainingContract2.MetadataConstraints);
263             Assert.NotEqual(originalContract.MetadataConstraints, remainingContract2.MetadataConstraints);
264         }
265
266         [Fact]
267         public void TryUnwrapMetadataConstraint_NullContractName_ThrowsArgumentNullException()
268         {
269             var contract = new CompositionContract(typeof(int));
270             AssertExtensions.Throws<ArgumentNullException>("constraintName", () => contract.TryUnwrapMetadataConstraint(null, out int unusedValue, out CompositionContract unusedContract));
271         }
272
273         public static IEnumerable<object[]> ToString_TestData()
274         {
275             yield return new object[] { new CompositionContract(typeof(int)), "Int32" };
276             yield return new object[] { new CompositionContract(typeof(int), "contractName"), "Int32 \"contractName\"" };
277             yield return new object[] { new CompositionContract(typeof(List<>), "contractName", new Dictionary<string, object> { { "key1", "value" }, { "key2", 2 } }), "List`1 \"contractName\" { key1 = \"value\", key2 = 2 }" };
278             yield return new object[] { new CompositionContract(typeof(List<string>), "contractName", new Dictionary<string, object> { { "key1", "value" }, { "key2", 2 } }), "List<String> \"contractName\" { key1 = \"value\", key2 = 2 }" };
279         }
280
281         [Theory]
282         [MemberData(nameof(ToString_TestData))]
283         public void ToString_Get_ReturnsExpected(CompositionContract contract, string expected)
284         {
285             Assert.Equal(expected, contract.ToString());
286         }
287
288         [Fact]
289         public void ToString_NullValueInDictionary_ThrowsArgumentNullException()
290         {
291             var contract = new CompositionContract(typeof(int), "contractName", new Dictionary<string, object> { { "key", null } });
292             AssertExtensions.Throws<ArgumentNullException>("value", () => contract.ToString());
293         }
294
295         [Fact]
296         public void ToString_NullTypeInGenericTypeArguments_ThrowsArgumentNullException()
297         {
298             var contract = new CompositionContract(new SubType() { GenericTypeArgumentsOverride = new Type[] { null } });
299             AssertExtensions.Throws<ArgumentNullException>("type", () => contract.ToString());
300         }
301
302         private class SubType : Type
303         {
304             public override Assembly Assembly => throw new NotImplementedException();
305             public override string AssemblyQualifiedName => throw new NotImplementedException();
306             public override Type BaseType => throw new NotImplementedException();
307             public override string FullName => throw new NotImplementedException();
308             public override Guid GUID => throw new NotImplementedException();
309             public override Module Module => throw new NotImplementedException();
310             public override string Namespace => throw new NotImplementedException();
311             public override Type UnderlyingSystemType => throw new NotImplementedException();
312
313             public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) => throw new NotImplementedException();
314             public override object[] GetCustomAttributes(bool inherit) => throw new NotImplementedException();
315             public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotImplementedException();
316             public override Type GetElementType() => throw new NotImplementedException();
317             public override EventInfo GetEvent(string name, BindingFlags bindingAttr) => throw new NotImplementedException();
318             public override EventInfo[] GetEvents(BindingFlags bindingAttr) => throw new NotImplementedException();
319             public override FieldInfo GetField(string name, BindingFlags bindingAttr) => throw new NotImplementedException();
320             public override FieldInfo[] GetFields(BindingFlags bindingAttr) => throw new NotImplementedException();
321             public override Type GetInterface(string name, bool ignoreCase) => throw new NotImplementedException();
322             public override Type[] GetInterfaces() => throw new NotImplementedException();
323             public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => throw new NotImplementedException();
324             public override MethodInfo[] GetMethods(BindingFlags bindingAttr) => throw new NotImplementedException();
325             public override Type GetNestedType(string name, BindingFlags bindingAttr) => throw new NotImplementedException();
326             public override Type[] GetNestedTypes(BindingFlags bindingAttr) => throw new NotImplementedException();
327             public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) => throw new NotImplementedException();
328             public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotImplementedException();
329             public override bool IsDefined(Type attributeType, bool inherit) => throw new NotImplementedException();
330             protected override TypeAttributes GetAttributeFlagsImpl() => throw new NotImplementedException();
331             protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotImplementedException();
332             protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) => throw new NotImplementedException();
333             protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotImplementedException();
334
335             protected override bool HasElementTypeImpl() => throw new NotImplementedException();
336             protected override bool IsArrayImpl() => throw new NotImplementedException();
337             protected override bool IsByRefImpl() => throw new NotImplementedException();
338             protected override bool IsCOMObjectImpl() => throw new NotImplementedException();
339             protected override bool IsPointerImpl() => throw new NotImplementedException();
340             protected override bool IsPrimitiveImpl() => throw new NotImplementedException();
341
342             public override string Name => "Name`1";
343             public override bool IsConstructedGenericType => true;
344             public Type[] GenericTypeArgumentsOverride { get; set; }
345             public override Type[] GenericTypeArguments => GenericTypeArgumentsOverride;
346         }
347     }
348 }