Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'linker/Tests/TestCasesRunner/AssemblyChecker.cs')
-rw-r--r--linker/Tests/TestCasesRunner/AssemblyChecker.cs323
1 files changed, 323 insertions, 0 deletions
diff --git a/linker/Tests/TestCasesRunner/AssemblyChecker.cs b/linker/Tests/TestCasesRunner/AssemblyChecker.cs
new file mode 100644
index 000000000..e9ed80abc
--- /dev/null
+++ b/linker/Tests/TestCasesRunner/AssemblyChecker.cs
@@ -0,0 +1,323 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Mono.Cecil;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Extensions;
+using NUnit.Framework;
+
+namespace Mono.Linker.Tests.TestCasesRunner {
+ class AssemblyChecker {
+ readonly AssemblyDefinition originalAssembly, linkedAssembly;
+
+ HashSet<string> linkedMembers;
+ HashSet<string> verifiedBackingFields = new HashSet<string> ();
+
+ public AssemblyChecker (AssemblyDefinition original, AssemblyDefinition linked)
+ {
+ this.originalAssembly = original;
+ this.linkedAssembly = linked;
+ }
+
+ public void Verify ()
+ {
+ // TODO: Implement fully, probably via custom Kept attribute
+ Assert.IsFalse (linkedAssembly.MainModule.HasExportedTypes);
+
+ VerifyCustomAttributes (linkedAssembly, originalAssembly);
+
+ linkedMembers = new HashSet<string> (linkedAssembly.MainModule.AllMembers ().Select (s => {
+ return s.FullName;
+ }), StringComparer.Ordinal);
+
+ var membersToAssert = originalAssembly.MainModule.Types;
+ foreach (var originalMember in membersToAssert) {
+ var td = originalMember as TypeDefinition;
+ if (td != null) {
+ if (td.Name == "<Module>") {
+ linkedMembers.Remove (td.Name);
+ continue;
+ }
+
+ TypeDefinition linkedType = linkedAssembly.MainModule.GetType (originalMember.FullName);
+ VerifyTypeDefinition (td, linkedType);
+ linkedMembers.Remove (td.FullName);
+
+ continue;
+ }
+
+ throw new NotImplementedException ($"Don't know how to check member of type {originalMember.GetType ()}");
+ }
+
+ Assert.IsEmpty (linkedMembers, "Linked output includes unexpected member");
+ }
+
+ protected virtual void VerifyTypeDefinition (TypeDefinition original, TypeDefinition linked)
+ {
+ ModuleDefinition linkedModule = linked?.Module;
+
+ //
+ // Little bit complex check to allow easier test writting to match
+ // - It has [Kept] attribute or any variation of it
+ // - It contains Main method
+ // - It contains at least one member which has [Kept] attribute (not recursive)
+ //
+ bool expectedKept =
+ original.HasAttributeDerivedFrom (nameof (KeptAttribute)) ||
+ (linked != null && linkedModule.Assembly.EntryPoint.DeclaringType == linked) ||
+ original.AllMembers ().Any (l => l.HasAttribute (nameof (KeptAttribute)));
+
+ if (!expectedKept) {
+ if (linked != null)
+ Assert.Fail ($"Type `{original}' should have been removed");
+
+ return;
+ }
+
+ if (linked == null)
+ Assert.Fail ($"Type `{original}' should have been kept");
+
+ if (!original.IsInterface)
+ VerifyBaseType (original, linked);
+
+ VerifyInterfaces (original, linked);
+
+ VerifyGenericParameters (original, linked);
+ VerifyCustomAttributes (original, linked);
+
+ foreach (var td in original.NestedTypes) {
+ VerifyTypeDefinition (td, linked?.NestedTypes.FirstOrDefault (l => td.FullName == l.FullName));
+ linkedMembers.Remove (td.FullName);
+ }
+
+ // Need to check properties before fields so that the KeptBackingFieldAttribute is handled correctly
+ foreach (var p in original.Properties) {
+ VerifyProperty (p, linked?.Properties.FirstOrDefault (l => p.Name == l.Name), linked);
+ linkedMembers.Remove (p.FullName);
+ }
+
+ foreach (var f in original.Fields) {
+ if (verifiedBackingFields.Contains (f.FullName))
+ continue;
+ VerifyField (f, linked?.Fields.FirstOrDefault (l => f.Name == l.Name));
+ linkedMembers.Remove (f.FullName);
+ }
+
+ foreach (var m in original.Methods) {
+ VerifyMethod (m, linked?.Methods.FirstOrDefault (l => m.GetSignature () == l.GetSignature ()));
+ linkedMembers.Remove (m.FullName);
+ }
+
+ foreach (var e in original.Events) {
+ VerifyEvent (e, linked?.Events.FirstOrDefault (l => e.Name == l.Name));
+ linkedMembers.Remove (e.FullName);
+ }
+ }
+
+ void VerifyBaseType (TypeDefinition src, TypeDefinition linked)
+ {
+ string expectedBaseName;
+ var expectedBaseGenericAttr = src.CustomAttributes.FirstOrDefault (w => w.AttributeType.Name == nameof (KeptBaseTypeAttribute) && w.ConstructorArguments.Count > 1);
+ if (expectedBaseGenericAttr != null) {
+ StringBuilder builder = new StringBuilder ();
+ builder.Append (expectedBaseGenericAttr.ConstructorArguments [0].Value);
+ builder.Append ("<");
+ bool separator = false;
+ foreach (var caa in (CustomAttributeArgument[])expectedBaseGenericAttr.ConstructorArguments [1].Value) {
+ if (separator)
+ builder.Append (",");
+ else
+ separator = true;
+
+ var arg = (CustomAttributeArgument)caa.Value;
+ builder.Append (arg.Value);
+ }
+
+ builder.Append (">");
+ expectedBaseName = builder.ToString ();
+ } else {
+ expectedBaseName = GetCustomAttributeCtorValues<object> (src, nameof (KeptBaseTypeAttribute)).FirstOrDefault ()?.ToString () ?? "System.Object";
+ }
+ Assert.AreEqual (expectedBaseName, linked.BaseType?.FullName);
+ }
+
+ void VerifyInterfaces (TypeDefinition src, TypeDefinition linked)
+ {
+ var expectedInterfaces = new HashSet<string> (GetCustomAttributeCtorValues<object> (src, nameof (KeptInterfaceAttribute)).Select (val => val.ToString ()));
+ if (expectedInterfaces.Count == 0) {
+ Assert.IsFalse (linked.HasInterfaces, $"Type `{src}' has unexpected interfaces");
+ } else {
+ foreach (var iface in linked.Interfaces) {
+ Assert.IsTrue (expectedInterfaces.Remove (iface.InterfaceType.FullName), $"Type `{src}' interface `{iface.InterfaceType.FullName}' should have been removed");
+ }
+
+ Assert.IsEmpty (expectedInterfaces);
+ }
+ }
+
+ void VerifyField (FieldDefinition src, FieldDefinition linked)
+ {
+ bool expectedKept = ShouldBeKept (src);
+
+ if (!expectedKept) {
+ if (linked != null)
+ Assert.Fail ($"Field `{src}' should have been removed");
+
+ return;
+ }
+
+ VerifyFieldKept (src, linked);
+ }
+
+ void VerifyFieldKept (FieldDefinition src, FieldDefinition linked)
+ {
+ if (linked == null)
+ Assert.Fail ($"Field `{src}' should have been kept");
+
+ Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Field `{src}' attributes");
+ Assert.AreEqual (src?.Constant, linked?.Constant, $"Field `{src}' value");
+
+ VerifyCustomAttributes (src, linked);
+ }
+
+ void VerifyProperty (PropertyDefinition src, PropertyDefinition linked, TypeDefinition linkedType)
+ {
+ VerifyBackingField (src, linkedType);
+
+ bool expectedKept = ShouldBeKept (src);
+
+ if (!expectedKept) {
+ if (linked != null)
+ Assert.Fail ($"Property `{src}' should have been removed");
+
+ return;
+ }
+
+ if (linked == null)
+ Assert.Fail ($"Property `{src}' should have been kept");
+
+ Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Property `{src}' attributes");
+ Assert.AreEqual (src?.Constant, linked?.Constant, $"Property `{src}' value");
+
+ VerifyCustomAttributes (src, linked);
+ }
+
+ void VerifyEvent (EventDefinition src, EventDefinition linked)
+ {
+ bool expectedKept = ShouldBeKept (src);
+
+ if (!expectedKept) {
+ if (linked != null)
+ Assert.Fail ($"Event `{src}' should have been removed");
+
+ return;
+ }
+
+ if (linked == null)
+ Assert.Fail ($"Event `{src}' should have been kept");
+
+ Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Event `{src}' attributes");
+
+ VerifyCustomAttributes (src, linked);
+ }
+
+ void VerifyMethod (MethodDefinition src, MethodDefinition linked)
+ {
+ var srcSignature = src.GetSignature ();
+ bool expectedKept = ShouldBeKept (src, srcSignature) || (linked != null && linked.DeclaringType.Module.EntryPoint == linked);
+
+ if (!expectedKept) {
+ if (linked != null)
+ Assert.Fail ($"Method `{src.FullName}' should have been removed");
+
+ return;
+ }
+
+ if (linked == null)
+ Assert.Fail ($"Method `{src.FullName}' should have been kept");
+
+ Assert.AreEqual (src?.Attributes, linked?.Attributes, $"Method `{src}' attributes");
+
+ VerifyGenericParameters (src, linked);
+ VerifyCustomAttributes (src, linked);
+ }
+
+ void VerifyBackingField (PropertyDefinition src, TypeDefinition linkedType)
+ {
+ var keptBackingFieldAttribute = src.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == nameof (KeptBackingFieldAttribute));
+ if (keptBackingFieldAttribute == null)
+ return;
+
+ var backingFieldName = $"<{src.Name}>k__BackingField";
+ var srcField = src.DeclaringType.Fields.FirstOrDefault (f => f.Name == backingFieldName);
+
+ if (srcField == null) {
+ // Can add more here if necessary
+ backingFieldName = backingFieldName.Replace ("System.Int32", "int");
+ backingFieldName = backingFieldName.Replace ("System.String", "string");
+ backingFieldName = backingFieldName.Replace ("System.Char", "char");
+
+ srcField = src.DeclaringType.Fields.FirstOrDefault (f => f.Name == backingFieldName);
+ }
+
+ if (srcField == null)
+ Assert.Fail ($"Property `{src}', could not locate the expected backing field {backingFieldName}");
+
+ VerifyFieldKept (srcField, linkedType?.Fields.FirstOrDefault (l => srcField.Name == l.Name));
+ verifiedBackingFields.Add (srcField.FullName);
+ linkedMembers.Remove (srcField.FullName);
+ }
+
+ static void VerifyCustomAttributes (ICustomAttributeProvider src, ICustomAttributeProvider linked)
+ {
+ var expectedAttrs = new List<string> (GetCustomAttributeCtorValues<string> (src, nameof (KeptAttributeAttribute)));
+ var linkedAttrs = new List<string> (FilterLinkedAttributes (linked));
+
+ // FIXME: Linker unused attributes removal is not working
+ // Assert.That (linkedAttrs, Is.EquivalentTo (expectedAttrs), $"Custom attributes on `{src}' are not matching");
+ }
+
+ static IEnumerable<string> FilterLinkedAttributes (ICustomAttributeProvider linked)
+ {
+ foreach (var attr in linked.CustomAttributes) {
+ switch (attr.AttributeType.FullName) {
+ case "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute":
+ continue;
+ }
+
+ yield return attr.AttributeType.FullName;
+ }
+ }
+
+ static void VerifyGenericParameters (IGenericParameterProvider src, IGenericParameterProvider linked)
+ {
+ Assert.AreEqual (src.HasGenericParameters, linked.HasGenericParameters);
+ if (src.HasGenericParameters) {
+ for (int i = 0; i < src.GenericParameters.Count; ++i) {
+ // TODO: Verify constraints
+ VerifyCustomAttributes (src.GenericParameters [i], linked.GenericParameters [i]);
+ }
+ }
+ }
+
+ static bool ShouldBeKept<T> (T member, string signature = null) where T : MemberReference, ICustomAttributeProvider
+ {
+ if (member.HasAttribute (nameof (KeptAttribute)))
+ return true;
+
+ ICustomAttributeProvider cap = (ICustomAttributeProvider)member.DeclaringType;
+ if (cap == null)
+ return false;
+
+ return GetCustomAttributeCtorValues<string> (cap, nameof (KeptMemberAttribute)).Any (a => a == (signature ?? member.Name));
+ }
+
+ static IEnumerable<T> GetCustomAttributeCtorValues<T> (ICustomAttributeProvider provider, string attributeName) where T : class
+ {
+ return provider.CustomAttributes.
+ Where (w => w.AttributeType.Name == attributeName && w.Constructor.Parameters.Count == 1).
+ Select (l => l.ConstructorArguments [0].Value as T);
+ }
+ }
+}