[cecil-tests] Improve the obsolete member test. Fixes #13621. (#14258)

* Check implementation assemblies instead of reference assemblies.
* Try to print the source code location for failing API (this required processing
  the implementation assemblies, because the reference assemblies don't have debug
  information where the source code location is stored).
* Don't report API that has [EditorBrowsable (None)] attributes, presumably we
  decided to keep these for compatibility, while highly discouraging their
  continued use. Also stop doing this for the next time we can do a breaking
  change, maybe we can remove these APIs then.
* Don't report API that has [UnsupportedOSPlatform ("...#.#") attributes (with
  a version number), presumably this is API that is still valid for some OS
  versions.
* Enable the test for all APIs (no ignores anymore). It's green!

Fixes https://github.com/xamarin/xamarin-macios/issues/13621.
This commit is contained in:
Rolf Bjarne Kvinge 2022-02-28 11:26:44 +01:00 коммит произвёл GitHub
Родитель 4bda73fc82
Коммит df491ed4b3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 93 добавлений и 15 удалений

Просмотреть файл

@ -19,7 +19,7 @@ namespace Cecil.Tests {
static Dictionary<string, AssemblyDefinition> cache = new Dictionary<string, AssemblyDefinition> ();
// make sure we load assemblies only once into memory
public static AssemblyDefinition? GetAssembly (string assembly, ReaderParameters? parameters = null)
public static AssemblyDefinition? GetAssembly (string assembly, ReaderParameters? parameters = null, bool readSymbols = false)
{
if (!File.Exists (assembly))
return null;
@ -29,6 +29,7 @@ namespace Cecil.Tests {
resolver.AddSearchDirectory (GetBCLDirectory (assembly));
parameters = new ReaderParameters () {
AssemblyResolver = resolver,
ReadSymbols = readSymbols,
};
}
@ -179,6 +180,8 @@ namespace Cecil.Tests {
public static IEnumerable NetPlatformAssemblies => Configuration.GetRefLibraries ();
public static IEnumerable NetPlatformImplementationAssemblies => Configuration.GetBaseLibraryImplementations ();
public static IEnumerable TaskAssemblies {
get {
yield return CreateTestFixtureDataFromPath (Path.Combine (Configuration.SdkRootXI, "lib", "msbuild", "iOS", "Xamarin.iOS.Tasks.dll"));

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using NUnit.Framework;
@ -13,40 +14,60 @@ namespace Cecil.Tests {
[TestFixture]
public class ObsoleteTest {
[TestCaseSource (typeof (Helper), "NetPlatformAssemblies")] // call this method with every .net6 library
[TestCaseSource (typeof (Helper), nameof (Helper.NetPlatformImplementationAssemblies))] // call this method with every .net6 library
public void GetAllObsoletedThings (string assemblyPath)
{
var assembly = Helper.GetAssembly (assemblyPath);
if (assembly is null) {
Assert.Ignore ("{assemblyPath} could not be found (might be disabled in build)");
return;
}
Console.WriteLine (assemblyPath);
var assembly = Helper.GetAssembly (assemblyPath, readSymbols: true)!;
Assert.That (assembly, Is.Not.Null, "Must find the assembly");
// Make a list of Obsolete things
HashSet<string> found = new HashSet<string> ();
var found = new HashSet<string> ();
foreach (var prop in Helper.FilterProperties (assembly, a => HasObsoleteAttribute (a))) {
foreach (var prop in Helper.FilterProperties (assembly, a => FilterMember (a))) {
if (Skip (prop))
continue;
Console.WriteLine ($"{GetLocation (prop.GetMethod ?? prop.SetMethod)} {prop.FullName}");
found.Add (prop.FullName);
}
foreach (var meth in Helper.FilterMethods (assembly, a => HasObsoleteAttribute (a))) {
foreach (var meth in Helper.FilterMethods (assembly, a => FilterMember (a))) {
if (Skip (meth))
continue;
Console.WriteLine ($"{GetLocation (meth)} {meth.FullName}");
found.Add (meth.FullName);
}
foreach (var type in Helper.FilterTypes (assembly, a => HasObsoleteAttribute (a))) {
foreach (var type in Helper.FilterTypes (assembly, a => FilterMember (a))) {
if (Skip (type))
continue;
Console.WriteLine ($"{GetLocation (type.Methods.FirstOrDefault ())} {type.FullName}");
found.Add (type.FullName);
}
// TODO: Events?
Assert.That (found, Is.Empty, "Obsolete API");
}
Assert.That (found, Is.Empty, string.Join (Environment.NewLine, found));
bool FilterMember (ICustomAttributeProvider provider)
{
// If an API isn't obsolete, it's not under scrutiny from this test.
if (!HasObsoleteAttribute (provider))
return false;
// If the API has an UnsupportedOSPlatform attribute with a version, it means the API is available
// on earlier OS versions, which means we can't remove it.
if (HasVersionedUnsupportedOSPlatformAttribute (provider))
return false;
#if !XAMCORE_5_0
// If we've hidden an API from the IDE, assume we've decided to keep the API for binary compatibility
// At least until the next time we can do breaking changes.
if (HasEditorBrowseableNeverAttribute (provider))
return false;
#endif
// I'm bad!
return true;
}
bool HasObsoleteAttribute (ICustomAttributeProvider provider) => HasObsoleteAttribute (provider.CustomAttributes);
@ -55,15 +76,62 @@ namespace Cecil.Tests {
bool IsObsoleteAttribute (CustomAttribute attribute) => attribute.AttributeType.Name == "Obsolete" || (attribute.AttributeType.Name == "ObsoleteAttribute");
bool HasVersionedUnsupportedOSPlatformAttribute (ICustomAttributeProvider provider)
{
if (provider?.HasCustomAttributes != true)
return false;
foreach (var attr in provider.CustomAttributes) {
if (attr.AttributeType.Name != "UnsupportedOSPlatformAttribute")
continue;
var platform = (string) attr.ConstructorArguments [0].Value;
// is this a platform string with a version?
foreach (var ch in platform) {
if (ch >= '0' && ch <= '9')
return true;
}
}
// no UnsupportedOSPlatform attribute with a version here
return false;
}
bool HasEditorBrowseableNeverAttribute (ICustomAttributeProvider provider)
{
if (provider?.HasCustomAttributes != true)
return false;
foreach (var attr in provider.CustomAttributes) {
if (attr.AttributeType.Name != "EditorBrowsableAttribute")
continue;
var state = (EditorBrowsableState) attr.ConstructorArguments [0].Value;
if (state == EditorBrowsableState.Never)
return true;
}
return false;
}
bool Skip (MemberReference member)
{
var ns = member.FullName.Split ('.') [0];
// Skipping all namespaces until issue https://github.com/xamarin/xamarin-macios/issues/13621 is fixed
switch (ns) {
default:
return true;
return false;
}
}
static string GetLocation (MethodDefinition method)
{
if (method is null)
return "<no location> ";
if (method.DebugInformation.HasSequencePoints) {
var seq = method.DebugInformation.SequencePoints [0];
return seq.Document.Url + ":" + seq.StartLine + " ";
}
return string.Empty;
}
}
}

Просмотреть файл

@ -760,6 +760,13 @@ namespace Xamarin.Tests
return variable.Split (new char [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}
public static IEnumerable<string> GetBaseLibraryImplementations ()
{
foreach (var platform in GetIncludedPlatforms (true))
foreach (var lib in GetBaseLibraryImplementations (platform))
yield return lib;
}
public static IEnumerable<string> GetBaseLibraryImplementations (ApplePlatform platform)
{
var runtimeIdentifiers = GetRuntimeIdentifiers (platform);