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