Skip to content

Switch to spliting our test work items by method instead of class. #44898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#if NET

using System.Diagnostics;
using System.Collections;

namespace Microsoft.DotNet.Cli.Utils
{
Expand Down
128 changes: 53 additions & 75 deletions test/HelixTasks/AssemblyScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;

namespace Microsoft.DotNet.SdkCustomHelix.Sdk
{
Expand Down Expand Up @@ -211,22 +212,25 @@ private static List<TypeInfo> GetTypeInfoList(string assemblyPath)
private static List<TypeInfo> GetTypeInfoList(MetadataReader reader)
{
var list = new List<TypeInfo>();
foreach (var handle in reader.TypeDefinitions)
foreach (var handle in reader.MethodDefinitions)
{
var type = reader.GetTypeDefinition(handle);
if (!IsValidIdentifier(reader, type.Name))
var method = reader.GetMethodDefinition(handle);

var name = reader.GetString(method.Name);
if (!IsValidIdentifier(reader, name))
{
continue;
}

var methodCount = GetMethodCount(reader, type);
if (!ShouldIncludeType(reader, type, methodCount))
if (!ShouldIncludeMethod(reader, method))
{
continue;
}

var fullName = GetFullName(reader, type);
list.Add(new TypeInfo(fullName, methodCount));
var type = reader.GetTypeDefinition(method.GetDeclaringType());
var fullName = GetFullName(reader, type) + "." + name;
list.Add(new TypeInfo(fullName, 1));

}

// Ensure we get classes back in a deterministic order.
Expand All @@ -235,69 +239,56 @@ private static List<TypeInfo> GetTypeInfoList(MetadataReader reader)
}

/// <summary>
/// Determine if this type should be one of the <c>class</c> values passed to xunit. This
/// code doesn't actually resolve base types or trace through inherrited Fact attributes
/// hence we have to error on the side of including types with no tests vs. excluding them.
/// Determine if this method method values passed to xunit. This doesn't check for skipping
/// Include any tests with the known theory and fact attributes
/// </summary>
private static bool ShouldIncludeType(MetadataReader reader, TypeDefinition type, int testMethodCount)
private static bool ShouldIncludeMethod(MetadataReader reader, MethodDefinition method)
{
// xunit only handles public, non-abstract, non-generic classes
var isPublic =
TypeAttributes.Public == (type.Attributes & TypeAttributes.VisibilityMask) ||
TypeAttributes.NestedPublic == (type.Attributes & TypeAttributes.VisibilityMask);
if (!isPublic ||
TypeAttributes.Abstract == (type.Attributes & TypeAttributes.Abstract) ||
type.GetGenericParameters().Count != 0 ||
TypeAttributes.Class != (type.Attributes & TypeAttributes.ClassSemanticsMask))
{
return false;
}

// Compiler generated types / methods have the shape of the heuristic that we are looking
// at here. Filter them out as well.
if (!IsValidIdentifier(reader, type.Name))
{
return false;
}

if (testMethodCount > 0)
{
return true;
}

// The case we still have to consider at this point is a class with 0 defined methods,
// inheritting from a class with > 0 defined test methods. That is a completely valid
// xunit scenario. For now we're just going to exclude types that inherit from object
// because they clearly don't fit that category.
return !(InheritsFromObject(reader, type) ?? false);
}

private static int GetMethodCount(MetadataReader reader, TypeDefinition type)
{
var count = 0;
foreach (var handle in type.GetMethods())
{
var methodDefinition = reader.GetMethodDefinition(handle);
if (methodDefinition.GetCustomAttributes().Count == 0 ||
!IsValidIdentifier(reader, methodDefinition.Name))
{
continue;
}
var methodAttributes = method.GetCustomAttributes();
bool isTestMethod = false;

if (MethodAttributes.Public != (methodDefinition.Attributes & MethodAttributes.Public))
foreach (var attributeHandle in methodAttributes)
{
continue;
var attribute = reader.GetCustomAttribute(attributeHandle);
var attributeConstructorHandle = attribute.Constructor;
MemberReference attributeConstructor;
if (attributeConstructorHandle.Kind == HandleKind.MemberReference)
{
attributeConstructor = reader.GetMemberReference((MemberReferenceHandle)attributeConstructorHandle);
}
else
{
continue;
}
var attributeType = reader.GetTypeReference((TypeReferenceHandle)attributeConstructor.Parent);
var attributeTypeName = reader.GetString(attributeType.Name);

if (attributeTypeName == "FactAttribute" ||
attributeTypeName == "TheoryAttribute" ||
attributeTypeName == "CoreMSBuildAndWindowsOnlyFactAttribute" ||
attributeTypeName == "CoreMSBuildAndWindowsOnlyTheoryAttribute" ||
attributeTypeName == "CoreMSBuildOnlyFactAttribute" ||
attributeTypeName == "CoreMSBuildOnlyTheoryAttribute" ||
attributeTypeName == "FullMSBuildOnlyFactAttribute" ||
attributeTypeName == "FullMSBuildOnlyTheoryAttribute" ||
attributeTypeName == "PlatformSpecificFact" ||
attributeTypeName == "PlatformSpecificTheory" ||
attributeTypeName == "RequiresMSBuildVersionFactAttribute" ||
attributeTypeName == "RequiresMSBuildVersionTheoryAttribute" ||
attributeTypeName == "RequiresSpecificFrameworkFactAttribute" ||
attributeTypeName == "RequiresSpecificFrameworkTheoryAttribute" ||
attributeTypeName == "WindowsOnlyRequiresMSBuildVersionFactAttribute" ||
attributeTypeName == "WindowsOnlyRequiresMSBuildVersionTheoryAttribute")
{
isTestMethod = true;
break;
}
}

count++;
}

return count;
return isTestMethod;
}

private static bool IsValidIdentifier(MetadataReader reader, StringHandle handle)
private static bool IsValidIdentifier(MetadataReader reader, String name)
{
var name = reader.GetString(handle);
for (int i = 0; i < name.Length; i++)
{
switch (name[i])
Expand All @@ -312,19 +303,6 @@ private static bool IsValidIdentifier(MetadataReader reader, StringHandle handle
return true;
}

private static bool? InheritsFromObject(MetadataReader reader, TypeDefinition type)
{
if (type.BaseType.Kind != HandleKind.TypeReference)
{
return null;
}

var typeRef = reader.GetTypeReference((TypeReferenceHandle)type.BaseType);
return
reader.GetString(typeRef.Namespace) == "System" &&
reader.GetString(typeRef.Name) == "Object";
}

private static string GetFullName(MetadataReader reader, TypeDefinition type)
{
var typeName = reader.GetString(type.Name);
Expand Down
Loading