xamarin-macios/tests/cecil-tests/TaskAssemblyTests.cs

172 строки
5.1 KiB
C#
Исходник Ответственный История

Этот файл содержит невидимые символы Юникода!

Этот файл содержит невидимые символы Юникода, которые могут быть отображены не так, как показано ниже. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы показать скрытые символы.

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Xamarin.Tests;
#nullable enable
namespace Cecil.Tests {
[TestFixture]
[TestFixtureSource (typeof (Helper), "TaskAssemblies")]
public class TaskAssemblyTests {
string assembly;
public TaskAssemblyTests (string assembly)
{
this.assembly = assembly;
}
bool IsTaskType (TypeDefinition? td)
{
if (td == null)
return false;
if (td.HasInterfaces) {
foreach (var iface in td.Interfaces) {
if (iface.InterfaceType.Namespace == "Microsoft.Build.Framework" && iface.InterfaceType.Name == "ITask")
return true;
}
}
return IsTaskType (td.BaseType?.Resolve ());
}
[Test]
public void EnsureOnlyCodeInBaseTasks ()
{
if (assembly.Contains ("Xamarin.Mac.Tasks.dll") && !Configuration.include_mac)
Assert.Ignore ("Ignore until Xamarin.Mac is re-enabled. Issue: https://github.com/xamarin/xamarin-macios/issues/9680");
var parameters = new ReaderParameters (ReadingMode.Deferred);
var resolver = new DefaultAssemblyResolver ();
resolver.AddSearchDirectory ("/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/msbuild/Current/bin");
parameters.AssemblyResolver = resolver;
var asm = Helper.GetAssembly (assembly, parameters)!;
var checking_types = new List<TypeDefinition> ();
var should_be_abstract = new List<string> ();
foreach (var type in asm.MainModule.Types.OrderBy (v => v.FullName)) {
if (!IsTaskType (type)) {
// Console.WriteLine ($" {type.FullName} does not implement ITask");
continue;
}
if (type.IsAbstract)
continue;
// All the Base and Core classes should be abstract
if (type.Name.EndsWith ("Base", StringComparison.Ordinal) || type.Name.EndsWith ("Core", StringComparison.Ordinal)) {
should_be_abstract.Add (type.FullName);
continue;
}
checking_types.Add (type);
}
var failures = new List<string> ();
foreach (var type in checking_types) {
// We don't care about default constructors
var methods = type.Methods.Where (v => {
if (v.IsConstructor && v.Parameters.Count == 0 && v.HasBody) {
var skipNop = new Func<Instruction, Instruction?> (v => {
if (v == null)
return null;
while (v.OpCode.Code == Code.Nop)
v = v.Next;
return v;
});
// There should be this sequence of instructions, otherwise the default constructor has user code, and shouldn't be ignored:
var ins = skipNop (v.Body.Instructions [0]);
if (ins?.OpCode.Code != Code.Ldarg_0)
return true;
ins = skipNop (ins.Next);
if (ins?.OpCode.Code != Code.Call)
return true;
ins = skipNop (ins.Next);
if (ins?.OpCode.Code != Code.Ret)
return true;
ins = skipNop (ins.Next);
if (ins != null)
return true;
return false;
}
return true;
});
var failed = methods.Any () || type.HasProperties || type.HasFields;
var known_failure = IsKnownFailure (type.FullName);
if (!failed) {
if (known_failure) {
failures.Add ($"{type.FullName} is marked as a known failure when it didn't fail. Maybe the list of known failures need to be updated?");
continue;
}
continue; // We successfully succeeded!
}
if (failed && known_failure) {
Console.WriteLine ($"⚠️ {type.FullName} is known failure");
continue;
}
// We failed, and this type is not a known failure
var sb = new StringBuilder ();
sb.AppendLine ($"{type.FullName} is not an empty type:");
if (methods.Any ()) {
sb.AppendLine ($" It has {methods.Count ()} methods:");
foreach (var method in methods)
sb.AppendLine ($" {method.FullName}");
}
if (type.HasProperties) {
sb.AppendLine ($" It has {type.Properties.Count} properties:");
foreach (var property in type.Properties)
sb.AppendLine ($" {property.Name}");
}
if (type.HasProperties) {
sb.AppendLine ($" It has {type.Fields.Count} fields:");
foreach (var field in type.Fields)
sb.AppendLine ($" {field.Name}");
}
failures.Add (sb.ToString ());
Console.WriteLine ($"❌ {sb}");
}
Assert.That (failures, Is.Empty, "Types with code");
Assert.That (checking_types.Count, Is.AtLeast (50), "Checked types"); // Make sure the initial type filtering doesn't filter away too much by mistake
Assert.That (should_be_abstract, Is.Empty, "Classes that should be abstract");
}
bool IsKnownFailure (string typename)
{
switch (typename) {
case "Xamarin.MacDev.Tasks.UnpackLibraryResources":
return true;
}
switch (Path.GetFileNameWithoutExtension (assembly)) {
case "Xamarin.iOS.Tasks":
// No other known failures
return false;
case "Xamarin.Mac.Tasks":
switch (typename) {
case "Xamarin.Mac.Tasks.CodesignVerify":
case "Xamarin.Mac.Tasks.IBTool":
case "Xamarin.Mac.Tasks.Metal":
case "Xamarin.Mac.Tasks.MetalLib":
return true;
}
return false;
default:
throw new NotImplementedException ($"Unknown assembly: {assembly}");
}
}
}
}