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;
6 using System.Collections.Generic;
7 using System.Linq;
8 using System.Reflection;
9 using Xunit;
10 
11 namespace System.Tests
12 {
13     public static partial class AttributeTests
14     {
15         [Fact]
DefaultEquality()16         public static void DefaultEquality()
17         {
18             var a1 = new ParentAttribute { Prop = 1 };
19             var a2 = new ParentAttribute { Prop = 42 };
20             var a3 = new ParentAttribute { Prop = 1 };
21 
22             var d1 = new ChildAttribute { Prop = 1 };
23             var d2 = new ChildAttribute { Prop = 42 };
24             var d3 = new ChildAttribute { Prop = 1 };
25 
26             var s1 = new GrandchildAttribute { Prop = 1 };
27             var s2 = new GrandchildAttribute { Prop = 42 };
28             var s3 = new GrandchildAttribute { Prop = 1 };
29 
30             var f1 = new ChildAttributeWithField { Prop = 1 };
31             var f2 = new ChildAttributeWithField { Prop = 42 };
32             var f3 = new ChildAttributeWithField { Prop = 1 };
33 
34             Assert.NotEqual(a1, a2);
35             Assert.NotEqual(a2, a3);
36             Assert.Equal(a1, a3);
37 
38             // The implementation of Attribute.Equals uses reflection to
39             // enumerate fields. On .NET core, we add `BindingFlags.DeclaredOnly`
40             // to fix a bug where an instance of a subclass of an attribute can
41             // be equal to an instance of the parent class.
42             // See https://github.com/dotnet/coreclr/pull/6240
43             Assert.Equal(PlatformDetection.IsFullFramework, d1.Equals(d2));
44             Assert.Equal(PlatformDetection.IsFullFramework, d2.Equals(d3));
45             Assert.Equal(d1, d3);
46 
47             Assert.Equal(PlatformDetection.IsFullFramework, s1.Equals(s2));
48             Assert.Equal(PlatformDetection.IsFullFramework, s2.Equals(s3));
49             Assert.Equal(s1, s3);
50 
51             Assert.Equal(PlatformDetection.IsFullFramework, f1.Equals(f2));
52             Assert.Equal(PlatformDetection.IsFullFramework, f2.Equals(f3));
53             Assert.Equal(f1, f3);
54 
55             Assert.NotEqual(d1, a1);
56             Assert.NotEqual(d2, a2);
57             Assert.NotEqual(d3, a3);
58             Assert.NotEqual(d1, a3);
59             Assert.NotEqual(d3, a1);
60 
61             Assert.NotEqual(d1, s1);
62             Assert.NotEqual(d2, s2);
63             Assert.NotEqual(d3, s3);
64             Assert.NotEqual(d1, s3);
65             Assert.NotEqual(d3, s1);
66 
67             Assert.NotEqual(f1, a1);
68             Assert.NotEqual(f2, a2);
69             Assert.NotEqual(f3, a3);
70             Assert.NotEqual(f1, a3);
71             Assert.NotEqual(f3, a1);
72         }
73 
74         [Fact]
DefaultHashCode()75         public static void DefaultHashCode()
76         {
77             var a1 = new ParentAttribute { Prop = 1 };
78             var a2 = new ParentAttribute { Prop = 42 };
79             var a3 = new ParentAttribute { Prop = 1 };
80 
81             var d1 = new ChildAttribute { Prop = 1 };
82             var d2 = new ChildAttribute { Prop = 42 };
83             var d3 = new ChildAttribute { Prop = 1 };
84 
85             var s1 = new GrandchildAttribute { Prop = 1 };
86             var s2 = new GrandchildAttribute { Prop = 42 };
87             var s3 = new GrandchildAttribute { Prop = 1 };
88 
89             var f1 = new ChildAttributeWithField { Prop = 1 };
90             var f2 = new ChildAttributeWithField { Prop = 42 };
91             var f3 = new ChildAttributeWithField { Prop = 1 };
92 
93             Assert.NotEqual(a1.GetHashCode(), 0);
94             Assert.NotEqual(a2.GetHashCode(), 0);
95             Assert.NotEqual(a3.GetHashCode(), 0);
96             Assert.NotEqual(d1.GetHashCode(), 0);
97             Assert.NotEqual(d2.GetHashCode(), 0);
98             Assert.NotEqual(d3.GetHashCode(), 0);
99             Assert.NotEqual(s1.GetHashCode(), 0);
100             Assert.NotEqual(s2.GetHashCode(), 0);
101             Assert.NotEqual(s3.GetHashCode(), 0);
102             Assert.Equal(f1.GetHashCode(), 0);
103             Assert.Equal(f2.GetHashCode(), 0);
104             Assert.Equal(f3.GetHashCode(), 0);
105 
106             Assert.NotEqual(a1.GetHashCode(), a2.GetHashCode());
107             Assert.NotEqual(a2.GetHashCode(), a3.GetHashCode());
108             Assert.Equal(a1.GetHashCode(), a3.GetHashCode());
109 
110             // The implementation of Attribute.GetHashCode uses reflection to
111             // enumerate fields. On .NET core, we add `BindingFlags.DeclaredOnly`
112             // to fix a bug where the hash code of a subclass of an attribute can
113             // be equal to an instance of the parent class.
114             // See https://github.com/dotnet/coreclr/pull/6240
115             Assert.Equal(PlatformDetection.IsFullFramework, s1.GetHashCode().Equals(s2.GetHashCode()));
116             Assert.Equal(PlatformDetection.IsFullFramework, s2.GetHashCode().Equals(s3.GetHashCode()));
117             Assert.Equal(s1.GetHashCode(), s3.GetHashCode());
118 
119             Assert.Equal(PlatformDetection.IsFullFramework, d1.GetHashCode().Equals(d2.GetHashCode()));
120             Assert.Equal(PlatformDetection.IsFullFramework, d2.GetHashCode().Equals(d3.GetHashCode()));
121             Assert.Equal(d1.GetHashCode(), d3.GetHashCode());
122 
123             Assert.Equal(f1.GetHashCode(), f2.GetHashCode());
124             Assert.Equal(f2.GetHashCode(), f3.GetHashCode());
125             Assert.Equal(f1.GetHashCode(), f3.GetHashCode());
126 
127             Assert.Equal(!PlatformDetection.IsFullFramework, d1.GetHashCode().Equals(a1.GetHashCode()));
128             Assert.Equal(!PlatformDetection.IsFullFramework, d2.GetHashCode().Equals(a2.GetHashCode()));
129             Assert.Equal(!PlatformDetection.IsFullFramework, d3.GetHashCode().Equals(a3.GetHashCode()));
130             Assert.Equal(!PlatformDetection.IsFullFramework, d1.GetHashCode().Equals(a3.GetHashCode()));
131             Assert.Equal(!PlatformDetection.IsFullFramework, d3.GetHashCode().Equals(a1.GetHashCode()));
132 
133             Assert.Equal(!PlatformDetection.IsFullFramework, d1.GetHashCode().Equals(s1.GetHashCode()));
134             Assert.Equal(!PlatformDetection.IsFullFramework, d2.GetHashCode().Equals(s2.GetHashCode()));
135             Assert.Equal(!PlatformDetection.IsFullFramework, d3.GetHashCode().Equals(s3.GetHashCode()));
136             Assert.Equal(!PlatformDetection.IsFullFramework, d1.GetHashCode().Equals(s3.GetHashCode()));
137             Assert.Equal(!PlatformDetection.IsFullFramework, d3.GetHashCode().Equals(s1.GetHashCode()));
138 
139             Assert.NotEqual(f1.GetHashCode(), a1.GetHashCode());
140             Assert.NotEqual(f2.GetHashCode(), a2.GetHashCode());
141             Assert.NotEqual(f3.GetHashCode(), a3.GetHashCode());
142             Assert.NotEqual(f1.GetHashCode(), a3.GetHashCode());
143             Assert.NotEqual(f3.GetHashCode(), a1.GetHashCode());
144         }
145 
146         class ParentAttribute : Attribute
147         {
148             public int Prop {get;set;}
149         }
150 
151         class ChildAttribute : ParentAttribute { }
152         class GrandchildAttribute : ChildAttribute { }
153         class ChildAttributeWithField : ParentAttribute
154         {
155             public int Field = 0;
156         }
157 
158         [Fact]
159         [StringValue("\uDFFF")]
160         [ActiveIssue("TFS 437293 - Invalid Utf8 string in custom attribute argument falls back to wrong value.", TargetFrameworkMonikers.UapAot)]
StringArgument_InvalidCodeUnits_FallbackUsed()161         public static void StringArgument_InvalidCodeUnits_FallbackUsed()
162         {
163             MethodInfo thisMethod = typeof(AttributeTests).GetTypeInfo().GetDeclaredMethod("StringArgument_InvalidCodeUnits_FallbackUsed");
164             Assert.NotNull(thisMethod);
165 
166             CustomAttributeData cad = thisMethod.CustomAttributes.Where(ca => ca.AttributeType == typeof(StringValueAttribute)).FirstOrDefault();
167             Assert.NotNull(cad);
168 
169             string stringArg = cad.ConstructorArguments[0].Value as string;
170             Assert.NotNull(stringArg);
171             Assert.Equal("\uFFFD\uFFFD", stringArg);
172         }
173 
Equals_TestData()174         public static IEnumerable<object[]> Equals_TestData()
175         {
176             yield return new object[] { new StringValueAttribute("hello"), new StringValueAttribute("hello"), true, true };
177             yield return new object[] { new StringValueAttribute("hello"), new StringValueAttribute("foo"), false, false };
178 
179             yield return new object[] { new StringValueIntValueAttribute("hello", 1), new StringValueIntValueAttribute("hello", 1), true, true };
180             yield return new object[] { new StringValueIntValueAttribute("hello", 1), new StringValueIntValueAttribute("hello", 2), false, true }; // GetHashCode() ignores the int value
181 
182             yield return new object[] { new EmptyAttribute(), new EmptyAttribute(), true, true };
183 
184             yield return new object[] { new StringValueAttribute("hello"), new StringValueIntValueAttribute("hello", 1), false, true }; // GetHashCode() ignores the int value
185             yield return new object[] { new StringValueAttribute("hello"), "hello", false, false };
186             yield return new object[] { new StringValueAttribute("hello"), null, false, false };
187         }
188 
189         [Theory]
190         [MemberData(nameof(Equals_TestData))]
Equals(Attribute attr1, object obj, bool expected, bool hashEqualityExpected)191         public static void Equals(Attribute attr1, object obj, bool expected, bool hashEqualityExpected)
192         {
193             Assert.Equal(expected, attr1.Equals(obj));
194 
195             Attribute attr2 = obj as Attribute;
196             if (attr2 != null)
197             {
198                 Assert.Equal(hashEqualityExpected, attr1.GetHashCode() == attr2.GetHashCode());
199             }
200         }
201 
202         [AttributeUsage(AttributeTargets.Method)]
203         private sealed class StringValueAttribute : Attribute
204         {
205             public string StringValue;
StringValueAttribute(string stringValue)206             public StringValueAttribute(string stringValue)
207             {
208                 StringValue = stringValue;
209             }
210         }
211 
212         private sealed class StringValueIntValueAttribute : Attribute
213         {
214             public string StringValue;
215             private int IntValue;
216 
StringValueIntValueAttribute(string stringValue, int intValue)217             public StringValueIntValueAttribute(string stringValue, int intValue)
218             {
219                 StringValue = stringValue;
220                 IntValue = intValue;
221             }
222         }
223 
224         [AttributeUsage(AttributeTargets.Method)]
225         private sealed class EmptyAttribute : Attribute { }
226 
227         [Fact]
ValidateDefaults()228         public static void ValidateDefaults()
229         {
230             StringValueAttribute sav =  new StringValueAttribute("test");
231             Assert.Equal(false, sav.IsDefaultAttribute());
232             Assert.Equal(sav.GetType(), sav.TypeId);
233             Assert.Equal(true, sav.Match(sav));
234         }
235     }
236 }
237