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
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs24
-rw-r--r--test/Mono.Linker.Tests.Cases.Expectations/Assertions/UnrecognizedReflectionAccessPatternAttribute.cs22
-rw-r--r--test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs3
-rw-r--r--test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs7
-rw-r--r--test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs5
-rw-r--r--test/Mono.Linker.Tests/Extensions/CecilExtensions.cs30
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/LinkerCustomizations.cs2
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs125
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs9
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/TestReflectionPatternRecorder.cs37
10 files changed, 262 insertions, 2 deletions
diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs
new file mode 100644
index 000000000..09d83aa65
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RecognizedReflectionAccessPatternAttribute.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
+ public class RecognizedReflectionAccessPatternAttribute : BaseExpectedLinkedBehaviorAttribute
+ {
+ public RecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, string reflectionMethodName, Type[] reflectionMethodParameters,
+ Type accessedItemType, string accessedItemName, Type[] accessedItemParameters)
+ {
+ if (reflectionMethodType == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodType));
+ if (reflectionMethodName == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodName));
+ if (reflectionMethodParameters == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodParameters));
+
+ if (accessedItemType == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (accessedItemType));
+ if (accessedItemName == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (accessedItemName));
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/UnrecognizedReflectionAccessPatternAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/UnrecognizedReflectionAccessPatternAttribute.cs
new file mode 100644
index 000000000..666c2da63
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/UnrecognizedReflectionAccessPatternAttribute.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
+ public class UnrecognizedReflectionAccessPatternAttribute : BaseExpectedLinkedBehaviorAttribute
+ {
+ public UnrecognizedReflectionAccessPatternAttribute (Type reflectionMethodType, string reflectionMethodName, Type [] reflectionMethodParameters,
+ string message = null)
+ {
+ if (reflectionMethodType == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodType));
+ if (reflectionMethodName == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodName));
+ if (reflectionMethodParameters == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (reflectionMethodParameters));
+
+ if (message == null)
+ throw new ArgumentException ("Value cannot be null or empty.", nameof (message));
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs
index 47c40dd6f..d1d0291d4 100644
--- a/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs
+++ b/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs
@@ -6,6 +6,8 @@ namespace Mono.Linker.Tests.Cases.Reflection
{
public class ActivatorCreateInstance
{
+ [UnrecognizedReflectionAccessPattern(
+ typeof(Activator), nameof(Activator.CreateInstance) + "<T>", new Type[0])]
public static void Main ()
{
Activator.CreateInstance (typeof (Test1));
@@ -13,6 +15,7 @@ namespace Mono.Linker.Tests.Cases.Reflection
Activator.CreateInstance (typeof (Test3), BindingFlags.NonPublic | BindingFlags.Instance, null, null, null);
Activator.CreateInstance (typeof (Test4), new object [] { 1, "ss" });
Activator.CreateInstance ("test", "Mono.Linker.Tests.Cases.Reflection.ActivatorCreateInstance+Test5");
+ Activator.CreateInstance<Test1> ();
}
[Kept]
diff --git a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs
index 39a7e12f4..01beb908b 100644
--- a/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs
+++ b/test/Mono.Linker.Tests.Cases/Reflection/MethodUsedViaReflection.cs
@@ -1,8 +1,13 @@
-using System.Reflection;
+using System;
+using System.Reflection;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
namespace Mono.Linker.Tests.Cases.Reflection {
+
public class MethodUsedViaReflection {
+ [RecognizedReflectionAccessPattern (
+ typeof (Type), nameof (Type.GetMethod), new Type [] { typeof(string), typeof(BindingFlags) },
+ typeof (MethodUsedViaReflection), nameof (MethodUsedViaReflection.OnlyCalledViaReflection), new Type [0])]
public static void Main ()
{
var method = typeof (MethodUsedViaReflection).GetMethod ("OnlyCalledViaReflection", BindingFlags.Static | BindingFlags.NonPublic);
diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs
index 1937b971a..26d2bb92e 100644
--- a/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs
+++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs
@@ -24,6 +24,8 @@ namespace Mono.Linker.Tests.Cases.Reflection {
}
[Kept]
+ [UnrecognizedReflectionAccessPattern (
+ typeof (Type), nameof (Type.GetType), new Type [] { typeof (string), typeof (bool) })]
public static void TestNull ()
{
const string reflectionTypeKeptString = null;
@@ -81,6 +83,9 @@ namespace Mono.Linker.Tests.Cases.Reflection {
public class AType { }
[Kept]
+ [RecognizedReflectionAccessPattern (
+ typeof (Type), nameof (Type.GetType), new Type[] { typeof (string), typeof (bool) },
+ typeof (AType), null, null)]
public static void TestType ()
{
const string reflectionTypeKeptString = "Mono.Linker.Tests.Cases.Reflection.TypeUsedViaReflection+AType";
diff --git a/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs b/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs
index 747abf570..60781468d 100644
--- a/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs
+++ b/test/Mono.Linker.Tests/Extensions/CecilExtensions.cs
@@ -56,6 +56,36 @@ namespace Mono.Linker.Tests.Extensions {
yield return @event;
}
+ public static IEnumerable<MethodDefinition> AllMethods (this TypeDefinition type)
+ {
+ foreach (var m in type.AllMembers()) {
+ switch (m) {
+ case MethodDefinition method:
+ yield return method;
+ break;
+ case PropertyDefinition @property:
+ if (@property.GetMethod != null)
+ yield return @property.GetMethod;
+
+ if (@property.SetMethod != null)
+ yield return @property.SetMethod;
+
+ break;
+ case EventDefinition @event:
+ if (@event.AddMethod != null)
+ yield return @event.AddMethod;
+
+ if (@event.RemoveMethod != null)
+ yield return @event.RemoveMethod;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
public static bool HasAttribute (this ICustomAttributeProvider provider, string name)
{
return provider.CustomAttributes.Any (ca => ca.AttributeType.Name == name);
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/LinkerCustomizations.cs b/test/Mono.Linker.Tests/TestCasesRunner/LinkerCustomizations.cs
index d0b65edec..bb599df7f 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/LinkerCustomizations.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/LinkerCustomizations.cs
@@ -10,6 +10,8 @@ namespace Mono.Linker.Tests.TestCasesRunner
{
public TestDependencyRecorder DependencyRecorder { get; set; }
+ public TestReflectionPatternRecorder ReflectionPatternRecorder { get; set; }
+
public event Action<LinkContext> CustomizeContext;
public void CustomizeLinkContext(LinkContext context)
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
index f2cf4efd1..bb1de9289 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
@@ -168,6 +168,7 @@ namespace Mono.Linker.Tests.TestCasesRunner {
{
VerifyLoggedMessages(original, linkResult.Logger);
VerifyRecordedDependencies (original, linkResult.Customizations.DependencyRecorder);
+ VerifyRecordedReflectionPatterns (original, linkResult.Customizations.ReflectionPatternRecorder);
}
protected virtual void InitialChecking (LinkedTestCaseResult linkResult, AssemblyDefinition original, AssemblyDefinition linked)
@@ -644,7 +645,7 @@ namespace Mono.Linker.Tests.TestCasesRunner {
$"Expected to find recorded dependency '{expectedSource} -> {expectedTarget} {expectedMarked ?? string.Empty}'{Environment.NewLine}" +
$"Potential dependencies matching the target: {Environment.NewLine}{targetCandidates}{Environment.NewLine}" +
$"Potential dependencies matching the source: {Environment.NewLine}{sourceCandidates}{Environment.NewLine}" +
- $"If there's no matches, try to specify just a part of the source/target name and rerun the test.");
+ $"If there's no matches, try to specify just a part of the source/target name and rerun the test to get potential matches.");
}
}
}
@@ -656,6 +657,128 @@ namespace Mono.Linker.Tests.TestCasesRunner {
}
}
+ void VerifyRecordedReflectionPatterns (AssemblyDefinition original, TestReflectionPatternRecorder reflectionPatternRecorder)
+ {
+ foreach (var expectedSourceMethodDefinition in original.MainModule.AllDefinedTypes ().SelectMany (t => t.AllMethods ())) {
+ foreach (var attr in expectedSourceMethodDefinition.CustomAttributes) {
+ if (attr.AttributeType.Resolve ().Name == nameof (RecognizedReflectionAccessPatternAttribute)) {
+ string expectedSourceMethod = GetFullMemberNameFromDefinition (expectedSourceMethodDefinition);
+ string expectedReflectionMethod = GetFullMemberNameFromReflectionAccessPatternAttribute (attr, constructorArgumentsOffset: 0);
+ string expectedAccessedItem = GetFullMemberNameFromReflectionAccessPatternAttribute (attr, constructorArgumentsOffset: 3);
+
+ if (!reflectionPatternRecorder.RecognizedPatterns.Any (pattern => {
+ if (GetFullMemberNameFromDefinition (pattern.SourceMethod) != expectedSourceMethod)
+ return false;
+
+ if (GetFullMemberNameFromDefinition (pattern.ReflectionMethod) != expectedReflectionMethod)
+ return false;
+
+ if (GetFullMemberNameFromDefinition (pattern.AccessedItem) != expectedAccessedItem)
+ return false;
+
+ return true;
+ })) {
+ string sourceMethodCandidates = string.Join (Environment.NewLine, reflectionPatternRecorder.RecognizedPatterns
+ .Where (p => GetFullMemberNameFromDefinition (p.SourceMethod).ToLowerInvariant ().Contains (expectedSourceMethod.ToLowerInvariant ()))
+ .Select (p => "\t" + RecognizedReflectionAccessPatternToString (p)));
+ string reflectionMethodCandidates = string.Join (Environment.NewLine, reflectionPatternRecorder.RecognizedPatterns
+ .Where (p => GetFullMemberNameFromDefinition (p.ReflectionMethod).ToLowerInvariant ().Contains (expectedReflectionMethod.ToLowerInvariant ()))
+ .Select (p => "\t" + RecognizedReflectionAccessPatternToString (p)));
+
+ Assert.Fail (
+ $"Expected to find recognized reflection access pattern '{expectedSourceMethod.ToString()}: Call to {expectedReflectionMethod} accessed {expectedAccessedItem}'{Environment.NewLine}" +
+ $"Potential patterns matching the source method: {Environment.NewLine}{sourceMethodCandidates}{Environment.NewLine}" +
+ $"Potential patterns matching the reflection method: {Environment.NewLine}{reflectionMethodCandidates}{Environment.NewLine}" +
+ $"If there's no matches, try to specify just a part of the source method or reflection method name and rerun the test to get potential matches.");
+ }
+ } else if (attr.AttributeType.Resolve ().Name == nameof (UnrecognizedReflectionAccessPatternAttribute)) {
+ string expectedSourceMethod = GetFullMemberNameFromDefinition (expectedSourceMethodDefinition);
+ string expectedReflectionMethod = GetFullMemberNameFromReflectionAccessPatternAttribute (attr, constructorArgumentsOffset: 0);
+ string expectedMessage = (string)attr.ConstructorArguments [3].Value;
+
+ if (!reflectionPatternRecorder.UnrecognizedPatterns.Any (pattern => {
+ if (GetFullMemberNameFromDefinition (pattern.SourceMethod) != expectedSourceMethod)
+ return false;
+
+ if (GetFullMemberNameFromDefinition (pattern.ReflectionMethod) != expectedReflectionMethod)
+ return false;
+
+ if (expectedMessage != null && pattern.Message != expectedMessage)
+ return false;
+
+ return true;
+ })) {
+ string sourceMethodCandidates = string.Join (Environment.NewLine, reflectionPatternRecorder.UnrecognizedPatterns
+ .Where (p => GetFullMemberNameFromDefinition (p.SourceMethod).ToLowerInvariant ().Contains (expectedSourceMethod.ToLowerInvariant ()))
+ .Select (p => "\t" + UnrecognizedReflectionAccessPatternToString (p)));
+ string reflectionMethodCandidates = string.Join (Environment.NewLine, reflectionPatternRecorder.UnrecognizedPatterns
+ .Where (p => GetFullMemberNameFromDefinition (p.ReflectionMethod).ToLowerInvariant ().Contains (expectedReflectionMethod.ToLowerInvariant ()))
+ .Select (p => "\t" + UnrecognizedReflectionAccessPatternToString (p)));
+
+ Assert.Fail (
+ $"Expected to find unrecognized reflection access pattern '{expectedSourceMethod}: Call to {expectedReflectionMethod} unrecognized {expectedMessage ?? string.Empty}'{Environment.NewLine}" +
+ $"Potential patterns matching the source method: {Environment.NewLine}{sourceMethodCandidates}{Environment.NewLine}" +
+ $"Potential patterns matching the reflection method: {Environment.NewLine}{reflectionMethodCandidates}{Environment.NewLine}" +
+ $"If there's no matches, try to specify just a part of the source method or reflection method name and rerun the test to get potential matches.");
+ }
+ }
+ }
+ }
+ }
+
+ static string GetFullMemberNameFromReflectionAccessPatternAttribute (CustomAttribute attr, int constructorArgumentsOffset)
+ {
+ var type = attr.ConstructorArguments [constructorArgumentsOffset].Value;
+ var memberName = (string)attr.ConstructorArguments [constructorArgumentsOffset + 1].Value;
+ var parameterTypes = (CustomAttributeArgument[])attr.ConstructorArguments [constructorArgumentsOffset + 2].Value;
+
+ string fullName = type.ToString ();
+ if (memberName == null) {
+ return fullName;
+ }
+
+ fullName += "::" + memberName;
+ if (parameterTypes != null) {
+ fullName += "(" + string.Join (",", parameterTypes.Select (t => t.Value.ToString ())) + ")";
+ }
+
+ return fullName;
+ }
+
+ static string GetFullMemberNameFromDefinition (IMemberDefinition member)
+ {
+ // Method which basically returns the same as member.ToString() but without the return type
+ // of a method (if it's a method).
+ // We need this as the GetFullMemberNameFromReflectionAccessPatternAttribute can't guess the return type
+ // as it would have to actually resolve the referenced method, which is very expensive and no necessary
+ // for the tests to work (the return types are redundant piece of information anyway).
+
+ if (member is TypeDefinition) {
+ return member.FullName;
+ }
+
+ string fullName = member.DeclaringType.FullName + "::";
+ if (member is MethodDefinition method) {
+ fullName += method.GetSignature ();
+ }
+ else {
+ fullName += member.Name;
+ }
+
+ return fullName;
+ }
+
+ static string RecognizedReflectionAccessPatternToString (TestReflectionPatternRecorder.ReflectionAccessPattern pattern)
+ {
+ return $"{GetFullMemberNameFromDefinition (pattern.SourceMethod)}: Call to {GetFullMemberNameFromDefinition (pattern.ReflectionMethod)} accessed {GetFullMemberNameFromDefinition (pattern.AccessedItem)}";
+ }
+
+ static string UnrecognizedReflectionAccessPatternToString (TestReflectionPatternRecorder.ReflectionAccessPattern pattern)
+ {
+ return $"{GetFullMemberNameFromDefinition (pattern.SourceMethod)}: Call to {GetFullMemberNameFromDefinition (pattern.ReflectionMethod)} unrecognized '{pattern.Message}'";
+ }
+
+
protected TypeDefinition GetOriginalTypeFromInAssemblyAttribute (CustomAttribute inAssemblyAttribute)
{
return GetOriginalTypeFromInAssemblyAttribute (inAssemblyAttribute.ConstructorArguments [0].Value.ToString (), inAssemblyAttribute.ConstructorArguments [1].Value);
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs
index d7a1d983d..7d0f22da1 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/TestCaseMetadaProvider.cs
@@ -75,6 +75,15 @@ namespace Mono.Linker.Tests.TestCasesRunner {
context.Tracer.AddRecorder (customizations.DependencyRecorder);
};
}
+
+ if (_testCaseTypeDefinition.AllMethods().Any(method => method.CustomAttributes.Any (attr =>
+ attr.AttributeType.Name == nameof (RecognizedReflectionAccessPatternAttribute) ||
+ attr.AttributeType.Name == nameof (UnrecognizedReflectionAccessPatternAttribute)))) {
+ customizations.ReflectionPatternRecorder = new TestReflectionPatternRecorder ();
+ customizations.CustomizeContext += context => {
+ context.ReflectionPatternRecorder = customizations.ReflectionPatternRecorder;
+ };
+ };
}
#if NETCOREAPP
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/TestReflectionPatternRecorder.cs b/test/Mono.Linker.Tests/TestCasesRunner/TestReflectionPatternRecorder.cs
new file mode 100644
index 000000000..88b78ec3b
--- /dev/null
+++ b/test/Mono.Linker.Tests/TestCasesRunner/TestReflectionPatternRecorder.cs
@@ -0,0 +1,37 @@
+using Mono.Cecil;
+using System.Collections.Generic;
+
+namespace Mono.Linker.Tests.TestCasesRunner
+{
+ public class TestReflectionPatternRecorder : IReflectionPatternRecorder
+ {
+ public struct ReflectionAccessPattern
+ {
+ public MethodDefinition SourceMethod;
+ public MethodDefinition ReflectionMethod;
+ public IMemberDefinition AccessedItem;
+ public string Message;
+ }
+
+ public List<ReflectionAccessPattern> RecognizedPatterns = new List<ReflectionAccessPattern> ();
+ public List<ReflectionAccessPattern> UnrecognizedPatterns = new List<ReflectionAccessPattern> ();
+
+ public void RecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, IMemberDefinition accessedItem)
+ {
+ RecognizedPatterns.Add (new ReflectionAccessPattern {
+ SourceMethod = sourceMethod,
+ ReflectionMethod = reflectionMethod,
+ AccessedItem = accessedItem
+ });
+ }
+
+ public void UnrecognizedReflectionAccessPattern (MethodDefinition sourceMethod, MethodDefinition reflectionMethod, string message)
+ {
+ UnrecognizedPatterns.Add (new ReflectionAccessPattern {
+ SourceMethod = sourceMethod,
+ ReflectionMethod = reflectionMethod,
+ Message = message
+ });
+ }
+ }
+}