From cf164f8ca2f4f6e0fe65bc9e02749ca7935d925a Mon Sep 17 00:00:00 2001
From: Paul Spangler <paul.spangler@ni.com>
Date: Wed, 10 Oct 2018 22:52:01 -0500
Subject: [PATCH 1/5] Apply TestCategory from derived class on inherited test
 methods

Enables sharing test methods across a class hierarchy while categorizing
the inherited tests based on the derived class that's actually running
them.
---
 .../Discovery/TypeEnumerator.cs               |  4 +--
 .../Helpers/ReflectHelper.cs                  | 26 ++++++++++---------
 .../Discovery/TypeEnumeratorTests.cs          |  2 +-
 .../Helpers/ReflectHelperTests.cs             | 13 +++++-----
 .../TestableReflectHelper.cs                  |  2 +-
 5 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
index 2e9260ef34..cbc90a9ff1 100644
--- a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
@@ -126,9 +126,9 @@ internal UnitTestElement GetTestFromMethod(MethodInfo method, bool isDeclaredInT
             var asyncTypeName = method.GetAsyncTypeName();
             testElement.AsyncTypeName = asyncTypeName;
 
-            testElement.TestCategory = this.reflectHelper.GetCategories(method);
+            testElement.TestCategory = this.reflectHelper.GetCategories(method, this.type);
 
-            testElement.DoNotParallelize = this.reflectHelper.IsDoNotParallelizeSet(method);
+            testElement.DoNotParallelize = this.reflectHelper.IsDoNotParallelizeSet(method, this.type);
 
             var traits = this.reflectHelper.GetTestPropertiesAsTraits(method);
 
diff --git a/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs b/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
index 94829b0270..2f19a74b08 100644
--- a/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
@@ -313,10 +313,11 @@ internal virtual bool IsMethodDeclaredInSameAssemblyAsType(MethodInfo method, Ty
         /// Get categories applied to the test method
         /// </summary>
         /// <param name="categoryAttributeProvider">The member to inspect.</param>
+        /// <param name="owningType">The reflected type that owns <paramref name="categoryAttributeProvider"/>.</param>
         /// <returns>Categories defined.</returns>
-        internal virtual string[] GetCategories(MemberInfo categoryAttributeProvider)
+        internal virtual string[] GetCategories(MemberInfo categoryAttributeProvider, Type owningType)
         {
-            var categories = this.GetCustomAttributesRecursively(categoryAttributeProvider, typeof(TestCategoryBaseAttribute));
+            var categories = this.GetCustomAttributesRecursively(categoryAttributeProvider, owningType, typeof(TestCategoryBaseAttribute));
             List<string> testCategories = new List<string>();
 
             if (categories != null)
@@ -344,11 +345,12 @@ internal ParallelizeAttribute GetParallelizeAttribute(Assembly assembly)
         /// Get the parallelization behavior for a test method.
         /// </summary>
         /// <param name="testMethod">Test method.</param>
+        /// <param name="owningType">The type that owns <paramref name="testMethod"/>.</param>
         /// <returns>True if test method should not run in parallel.</returns>
-        internal bool IsDoNotParallelizeSet(MemberInfo testMethod)
+        internal bool IsDoNotParallelizeSet(MemberInfo testMethod, Type owningType)
         {
             return this.GetCustomAttributes(testMethod, typeof(DoNotParallelizeAttribute)).Any()
-                   || this.GetCustomAttributes(testMethod.DeclaringType.GetTypeInfo(), typeof(DoNotParallelizeAttribute)).Any();
+                   || this.GetCustomAttributes(owningType.GetTypeInfo(), typeof(DoNotParallelizeAttribute)).Any();
         }
 
         /// <summary>
@@ -365,19 +367,20 @@ internal bool IsDoNotParallelizeSet(Assembly assembly)
         /// Gets custom attributes at the class and assembly for a method.
         /// </summary>
         /// <param name="attributeProvider">Method Info or Member Info or a Type</param>
+        /// <param name="owningType">The type that owns <paramref name="attributeProvider"/>.</param>
         /// <param name="type"> What type of CustomAttribute you need. For instance: TestCategory, Owner etc.,</param>
         /// <returns>The categories of the specified type on the method. </returns>
-        internal IEnumerable<object> GetCustomAttributesRecursively(MemberInfo attributeProvider, Type type)
+        internal IEnumerable<object> GetCustomAttributesRecursively(MemberInfo attributeProvider, Type owningType, Type type)
         {
             var categories = this.GetCustomAttributes(attributeProvider, typeof(TestCategoryBaseAttribute));
             if (categories != null)
             {
-                categories = categories.Concat(this.GetCustomAttributes(attributeProvider.DeclaringType.GetTypeInfo(), typeof(TestCategoryBaseAttribute))).ToArray();
+                categories = categories.Concat(this.GetCustomAttributes(owningType.GetTypeInfo(), typeof(TestCategoryBaseAttribute))).ToArray();
             }
 
             if (categories != null)
             {
-                categories = categories.Concat(this.GetCustomAttributeForAssembly(attributeProvider, typeof(TestCategoryBaseAttribute))).ToArray();
+                categories = categories.Concat(this.GetCustomAttributeForAssembly(owningType.GetTypeInfo().Assembly, typeof(TestCategoryBaseAttribute))).ToArray();
             }
 
             if (categories != null)
@@ -389,18 +392,17 @@ internal IEnumerable<object> GetCustomAttributesRecursively(MemberInfo attribute
         }
 
         /// <summary>
-        /// Gets the custom attributes on the assembly of a member info
+        /// Gets the custom attributes on an assembly
         /// NOTE: having it as separate virtual method, so that we can extend it for testing.
         /// </summary>
-        /// <param name="memberInfo">The member to inspect.</param>
+        /// <param name="assembly">The assembly to inspect.</param>
         /// <param name="type">The attribute type to find.</param>
         /// <returns>Custom attributes defined.</returns>
-        internal virtual Attribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo, Type type)
+        internal virtual Attribute[] GetCustomAttributeForAssembly(Assembly assembly, Type type)
         {
             return
                 PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(
-                    memberInfo.DeclaringType.GetTypeInfo().Assembly,
-                    type).OfType<Attribute>().ToArray();
+                    assembly, type).OfType<Attribute>().ToArray();
         }
 
         /// <summary>
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
index bc06bfaecf..fed5311e8d 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
@@ -247,7 +247,7 @@ public void GetTestFromMethodShouldSetTestCategory()
             var testCategories = new string[] { "foo", "bar" };
 
             // Setup mocks
-            this.mockReflectHelper.Setup(rh => rh.GetCategories(methodInfo)).Returns(testCategories);
+            this.mockReflectHelper.Setup(rh => rh.GetCategories(methodInfo, typeof(DummyTestClass))).Returns(testCategories);
 
             var testElement = typeEnumerator.GetTestFromMethod(methodInfo, true, this.warnings);
 
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
index 884e2d6c6b..4397b59a51 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
@@ -36,7 +36,6 @@ public void IntializeTests()
             this.reflectHelper = new TestableReflectHelper();
             this.method = new Mock<MethodInfo>();
             this.method.Setup(x => x.MemberType).Returns(MemberTypes.Method);
-            this.method.Setup(x => x.DeclaringType).Returns(typeof(ReflectHelperTests));
 
             this.testablePlatformServiceProvider = new TestablePlatformServiceProvider();
             this.testablePlatformServiceProvider.SetupMockReflectionOperations();
@@ -58,7 +57,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtClassLevel()
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel") }, MemberTypes.TypeInfo);
 
             string[] expected = new[] { "ClassLevel" };
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
 
             CollectionAssert.AreEqual(expected, actual);
         }
@@ -73,7 +72,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels()
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel") }, MemberTypes.TypeInfo);
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel") }, MemberTypes.Method);
 
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
             string[] expected = new[] { "MethodLevel", "ClassLevel", "AsmLevel" };
 
             CollectionAssert.AreEqual(expected, actual);
@@ -89,7 +88,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAssemblyLevel()
 
             string[] expected = new[] { "AsmLevel" };
 
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
 
             CollectionAssert.AreEqual(expected, actual);
         }
@@ -103,7 +102,7 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtClassLe
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel"), new UTF.TestCategoryAttribute("ClassLevel1") }, MemberTypes.TypeInfo);
 
             string[] expected = new[] { "ClassLevel", "ClassLevel1" };
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
 
             CollectionAssert.AreEqual(expected, actual);
         }
@@ -117,7 +116,7 @@ public void GetTestCategoryAttributeShouldIncludeMultipleTestCategoriesAtAssembl
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel"), new UTF.TestCategoryAttribute("AsmLevel1") }, MemberTypes.All);
 
             string[] expected = new[] { "AsmLevel", "AsmLevel1" };
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
             CollectionAssert.AreEqual(expected, actual);
         }
 
@@ -130,7 +129,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtMethodLevel()
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel") }, MemberTypes.Method);
 
             string[] expected = new[] { "MethodLevel" };
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
 
             CollectionAssert.AreEqual(expected, actual);
         }
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
index c09e9dba37..3da81a50ef 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
@@ -41,7 +41,7 @@ public void SetCustomAttribute(Type type, Attribute[] values, MemberTypes member
             }
         }
 
-        internal override Attribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo, Type type)
+        internal override Attribute[] GetCustomAttributeForAssembly(Assembly assembly, Type type)
         {
             var hashcode = MemberTypes.All.GetHashCode() + type.FullName.GetHashCode();
 

From 160eb6f50de4190a091509800a0c90a0c33953db Mon Sep 17 00:00:00 2001
From: Paul Spangler <paul.spangler@ni.com>
Date: Fri, 30 Nov 2018 00:14:22 -0600
Subject: [PATCH 2/5] Preserve DeclaringClassFullName from discovery to
 execution

---
 src/Adapter/MSTest.CoreAdapter/Constants.cs   |  3 ++
 .../MSTest.CoreAdapter/Execution/TypeCache.cs | 22 ++++++--
 .../Extensions/TestCaseExtensions.cs          |  6 +++
 .../ObjectModel/TestMethod.cs                 |  3 +-
 .../ObjectModel/UnitTestElement.cs            |  6 +++
 .../ObjectModel/ITestMethod.cs                |  3 +-
 .../Execution/TypeCacheTests.cs               | 51 +++++++++++++++++++
 .../Extensions/TestCaseExtensionsTests.cs     | 14 +++++
 .../ObjectModel/UnitTestElementTests.cs       | 14 +++++
 9 files changed, 117 insertions(+), 5 deletions(-)

diff --git a/src/Adapter/MSTest.CoreAdapter/Constants.cs b/src/Adapter/MSTest.CoreAdapter/Constants.cs
index 58bde36910..24a74c71c4 100644
--- a/src/Adapter/MSTest.CoreAdapter/Constants.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Constants.cs
@@ -32,6 +32,8 @@ internal static class Constants
 
         internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("MSTestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
 
+        internal static readonly TestProperty DeclaringClassNameProperty = TestProperty.Register("MSTestDiscoverer.DeclaringClassName", DeclaringClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
         internal static readonly TestProperty AsyncTestProperty = TestProperty.Register("MSTestDiscoverer.IsAsync", IsAsyncLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
 
 #pragma warning disable CS0618 // Type or member is obsolete
@@ -90,6 +92,7 @@ internal static class Constants
         /// These Property names should not be localized.
         /// </summary>
         private const string TestClassNameLabel = "ClassName";
+        private const string DeclaringClassNameLabel = "DeclaringClassName";
         private const string IsAsyncLabel = "IsAsync";
         private const string TestCategoryLabel = "TestCategory";
         private const string PriorityLabel = "Priority";
diff --git a/src/Adapter/MSTest.CoreAdapter/Execution/TypeCache.cs b/src/Adapter/MSTest.CoreAdapter/Execution/TypeCache.cs
index f2f659c2e3..4bd7e8785d 100644
--- a/src/Adapter/MSTest.CoreAdapter/Execution/TypeCache.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Execution/TypeCache.cs
@@ -605,10 +605,26 @@ private TestMethodAttribute GetTestMethodAttribute(MethodInfo methodInfo, TestCl
         private MethodInfo GetMethodInfoForTestMethod(TestMethod testMethod, TestClassInfo testClassInfo)
         {
             var methodsInClass = testClassInfo.ClassType.GetRuntimeMethods().ToArray();
+            MethodInfo testMethodInfo;
 
-            var testMethodInfo =
-                methodsInClass.Where(method => method.Name.Equals(testMethod.Name))
-                    .FirstOrDefault(method => method.HasCorrectTestMethodSignature(true));
+            if (testMethod.DeclaringClassFullName != null)
+            {
+                // Only find methods that match the given declaring name.
+                testMethodInfo =
+                    methodsInClass.Where(method => method.Name.Equals(testMethod.Name)
+                                                && method.DeclaringType.FullName.Equals(testMethod.DeclaringClassFullName)
+                                                && method.HasCorrectTestMethodSignature(true)).FirstOrDefault();
+            }
+            else
+            {
+                // Either the declaring class is the same as the test class, or
+                // the declaring class information wasn't passed in the test case.
+                // Prioritize the former while maintaining previous behavior for the latter.
+                var className = testClassInfo.ClassType.FullName;
+                testMethodInfo =
+                    methodsInClass.Where(method => method.Name.Equals(testMethod.Name) && method.HasCorrectTestMethodSignature(true))
+                        .OrderByDescending(method => method.DeclaringType.FullName.Equals(className)).FirstOrDefault();
+            }
 
             // if correct method is not found, throw appropriate
             // exception about what is wrong.
diff --git a/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs b/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs
index 45a17a14d7..4b36457406 100644
--- a/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Extensions/TestCaseExtensions.cs
@@ -22,9 +22,15 @@ internal static UnitTestElement ToUnitTestElement(this TestCase testCase, string
         {
             var isAsync = (testCase.GetPropertyValue(Constants.AsyncTestProperty) as bool?) ?? false;
             var testClassName = testCase.GetPropertyValue(Constants.TestClassNameProperty) as string;
+            var declaringClassName = testCase.GetPropertyValue(Constants.DeclaringClassNameProperty) as string;
 
             TestMethod testMethod = new TestMethod(testCase.DisplayName, testClassName, source, isAsync);
 
+            if (declaringClassName != null && declaringClassName != testClassName)
+            {
+                testMethod.DeclaringClassFullName = declaringClassName;
+            }
+
             UnitTestElement testElement = new UnitTestElement(testMethod)
                                         {
                                             IsAsync = isAsync,
diff --git a/src/Adapter/MSTest.CoreAdapter/ObjectModel/TestMethod.cs b/src/Adapter/MSTest.CoreAdapter/ObjectModel/TestMethod.cs
index 8692bd0b39..708e9b621f 100644
--- a/src/Adapter/MSTest.CoreAdapter/ObjectModel/TestMethod.cs
+++ b/src/Adapter/MSTest.CoreAdapter/ObjectModel/TestMethod.cs
@@ -74,7 +74,8 @@ public string DeclaringAssemblyName
         }
 
         /// <summary>
-        /// Gets or sets the declaring class full name. This will be used while getting navigation data.
+        /// Gets or sets the declaring class full name.
+        /// This will be used to resolve overloads and while getting navigation data.
         /// This will be null if FullClassName is same as DeclaringClassFullName.
         /// Reason to set to null in the above case is to minimise the transfer of data across appdomains and not have a perf hit.
         /// </summary>
diff --git a/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs
index 945fc8b57d..71df25f5ea 100644
--- a/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs
+++ b/src/Adapter/MSTest.CoreAdapter/ObjectModel/UnitTestElement.cs
@@ -94,6 +94,12 @@ internal TestCase ToTestCase()
 
             testCase.SetPropertyValue(TestAdapter.Constants.TestClassNameProperty, this.TestMethod.FullClassName);
 
+            // Set declaring type if present so the correct method info can be retrieved
+            if (this.TestMethod.DeclaringClassFullName != null)
+            {
+                testCase.SetPropertyValue(TestAdapter.Constants.DeclaringClassNameProperty, this.TestMethod.DeclaringClassFullName);
+            }
+
             // Many of the tests will not be async, so there is no point in sending extra data
             if (this.IsAsync)
             {
diff --git a/src/Adapter/PlatformServices.Interface/ObjectModel/ITestMethod.cs b/src/Adapter/PlatformServices.Interface/ObjectModel/ITestMethod.cs
index fd3abb6496..5d3abe84dd 100644
--- a/src/Adapter/PlatformServices.Interface/ObjectModel/ITestMethod.cs
+++ b/src/Adapter/PlatformServices.Interface/ObjectModel/ITestMethod.cs
@@ -19,7 +19,8 @@ public interface ITestMethod
         string FullClassName { get; }
 
         /// <summary>
-        /// Gets the declaring class full name. This will be used while getting navigation data.
+        /// Gets the declaring class full name.
+        /// This will be used for resolving overloads and while getting navigation data.
         /// </summary>
         string DeclaringClassFullName { get; }
 
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TypeCacheTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TypeCacheTests.cs
index db1c33b763..f14c20e450 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TypeCacheTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Execution/TypeCacheTests.cs
@@ -1031,6 +1031,48 @@ public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedTestClasses()
             Assert.IsNotNull(testMethodInfo.TestMethodOptions.Executor);
         }
 
+        [TestMethodV1]
+        public void GetTestMethodInfoShouldReturnTestMethodInfoForDerivedClassMethodOverloadByDefault()
+        {
+            var type = typeof(DerivedTestClass);
+            var methodInfo = type.GetRuntimeMethod("OverloadedTestMethod", new Type[] { });
+            var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false);
+
+            var testMethodInfo = this.typeCache.GetTestMethodInfo(
+                    testMethod,
+                    new TestContextImplementation(testMethod, null, new Dictionary<string, object>()),
+                    false);
+
+            Assert.AreEqual(methodInfo, testMethodInfo.TestMethod);
+            Assert.AreEqual(0, testMethodInfo.TestMethodOptions.Timeout);
+            Assert.AreEqual(this.typeCache.ClassInfoCache.ToArray()[0], testMethodInfo.Parent);
+            Assert.IsNotNull(testMethodInfo.TestMethodOptions.Executor);
+        }
+
+        [TestMethodV1]
+        public void GetTestMethodInfoShouldReturnTestMethodInfoForDeclaringTypeMethodOverload()
+        {
+            var baseType = typeof(BaseTestClass);
+            var type = typeof(DerivedTestClass);
+            var methodInfo = baseType.GetRuntimeMethod("OverloadedTestMethod", new Type[] { });
+            var testMethod = new TestMethod(methodInfo.Name, type.FullName, "A", isAsync: false)
+            {
+                DeclaringClassFullName = baseType.FullName
+            };
+
+            var testMethodInfo = this.typeCache.GetTestMethodInfo(
+                    testMethod,
+                    new TestContextImplementation(testMethod, null, new Dictionary<string, object>()),
+                    false);
+
+            // The two MethodInfo instances will have different ReflectedType properties,
+            // so cannot be compared directly. Use MethodHandle to verify it's the same.
+            Assert.AreEqual(methodInfo.MethodHandle, testMethodInfo.TestMethod.MethodHandle);
+            Assert.AreEqual(0, testMethodInfo.TestMethodOptions.Timeout);
+            Assert.AreEqual(this.typeCache.ClassInfoCache.ToArray()[0], testMethodInfo.Parent);
+            Assert.IsNotNull(testMethodInfo.TestMethodOptions.Executor);
+        }
+
         #endregion
 
         #endregion
@@ -1311,6 +1353,10 @@ public void TestMethodWithMultipleExpectedException()
         [DummyTestClass]
         internal class DerivedTestClass : BaseTestClass
         {
+            [UTF.TestMethod]
+            public new void OverloadedTestMethod()
+            {
+            }
         }
 
         internal class BaseTestClass
@@ -1319,6 +1365,11 @@ internal class BaseTestClass
             public void DummyTestMethod()
             {
             }
+
+            [UTF.TestMethod]
+            public void OverloadedTestMethod()
+            {
+            }
         }
 
         private class DummyTestClassWithNoDefaultConstructor
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs
index e827d03569..8c6ef2c73f 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Extensions/TestCaseExtensionsTests.cs
@@ -37,6 +37,7 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet()
             Assert.AreEqual("DummyDisplayName", resultUnitTestElement.TestMethod.Name);
             Assert.AreEqual("DummyClassName", resultUnitTestElement.TestMethod.FullClassName);
             Assert.AreEqual(true, resultUnitTestElement.TestMethod.IsAsync);
+            Assert.IsNull(resultUnitTestElement.TestMethod.DeclaringClassFullName);
         }
 
         [TestMethod]
@@ -52,5 +53,18 @@ public void ToUnitTestElementForTestCaseWithNoPropertiesShouldReturnUnitTestElem
             Assert.AreEqual(0, resultUnitTestElement.Priority);
             Assert.AreEqual(null, resultUnitTestElement.TestCategory);
         }
+
+        [TestMethod]
+        public void ToUnitTestElementShouldAddDeclaringClassNameToTestElementWhenAvailable()
+        {
+            TestCase testCase = new TestCase("DummyClass.DummyMethod", new Uri("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName);
+            testCase.SetPropertyValue(Constants.TestClassNameProperty, "DummyClassName");
+            testCase.SetPropertyValue(Constants.DeclaringClassNameProperty, "DummyDeclaringClassName");
+
+            var resultUnitTestElement = testCase.ToUnitTestElement(testCase.Source);
+
+            Assert.AreEqual("DummyClassName", resultUnitTestElement.TestMethod.FullClassName);
+            Assert.AreEqual("DummyDeclaringClassName", resultUnitTestElement.TestMethod.DeclaringClassFullName);
+        }
     }
 }
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs
index 7ccd716198..b831ee402d 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/ObjectModel/UnitTestElementTests.cs
@@ -84,6 +84,20 @@ public void ToTestCaseShouldSetTestClassNameProperty()
             Assert.AreEqual("C", testCase.GetPropertyValue(Constants.TestClassNameProperty));
         }
 
+        [TestMethodV1]
+        public void ToTestCaseShouldSetDeclaringClassNameIfPresent()
+        {
+            this.testMethod.DeclaringClassFullName = null;
+            var testCase = this.unitTestElement.ToTestCase();
+
+            Assert.IsNull(testCase.GetPropertyValue(Constants.DeclaringClassNameProperty));
+
+            this.testMethod.DeclaringClassFullName = "DC";
+            testCase = this.unitTestElement.ToTestCase();
+
+            Assert.AreEqual("DC", testCase.GetPropertyValue(Constants.DeclaringClassNameProperty));
+        }
+
         [TestMethodV1]
         public void ToTestCaseShouldSetIsAsyncProperty()
         {

From d587fa86fe7a84a049b89afbd18f0330fd68bf7a Mon Sep 17 00:00:00 2001
From: Paul Spangler <paul.spangler@ni.com>
Date: Fri, 30 Nov 2018 11:27:46 -0600
Subject: [PATCH 3/5] Restore GetCustomAttributeForAssembly taking in a
 MemberInfo, but reference the member's Module instead of DeclaringType

---
 .../MSTest.CoreAdapter/Helpers/ReflectHelper.cs        | 10 +++++-----
 .../TestableImplementations/TestableReflectHelper.cs   |  2 +-
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs b/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
index 2f19a74b08..5ebaed6cde 100644
--- a/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Helpers/ReflectHelper.cs
@@ -380,7 +380,7 @@ internal IEnumerable<object> GetCustomAttributesRecursively(MemberInfo attribute
 
             if (categories != null)
             {
-                categories = categories.Concat(this.GetCustomAttributeForAssembly(owningType.GetTypeInfo().Assembly, typeof(TestCategoryBaseAttribute))).ToArray();
+                categories = categories.Concat(this.GetCustomAttributeForAssembly(owningType.GetTypeInfo(), typeof(TestCategoryBaseAttribute))).ToArray();
             }
 
             if (categories != null)
@@ -392,17 +392,17 @@ internal IEnumerable<object> GetCustomAttributesRecursively(MemberInfo attribute
         }
 
         /// <summary>
-        /// Gets the custom attributes on an assembly
+        /// Gets the custom attributes on the assembly of a member info
         /// NOTE: having it as separate virtual method, so that we can extend it for testing.
         /// </summary>
-        /// <param name="assembly">The assembly to inspect.</param>
+        /// <param name="memberInfo">The member to inspect.</param>
         /// <param name="type">The attribute type to find.</param>
         /// <returns>Custom attributes defined.</returns>
-        internal virtual Attribute[] GetCustomAttributeForAssembly(Assembly assembly, Type type)
+        internal virtual Attribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo, Type type)
         {
             return
                 PlatformServiceProvider.Instance.ReflectionOperations.GetCustomAttributes(
-                    assembly, type).OfType<Attribute>().ToArray();
+                    memberInfo.Module.Assembly, type).OfType<Attribute>().ToArray();
         }
 
         /// <summary>
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
index 3da81a50ef..c09e9dba37 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/TestableImplementations/TestableReflectHelper.cs
@@ -41,7 +41,7 @@ public void SetCustomAttribute(Type type, Attribute[] values, MemberTypes member
             }
         }
 
-        internal override Attribute[] GetCustomAttributeForAssembly(Assembly assembly, Type type)
+        internal override Attribute[] GetCustomAttributeForAssembly(MemberInfo memberInfo, Type type)
         {
             var hashcode = MemberTypes.All.GetHashCode() + type.FullName.GetHashCode();
 

From 67658d132cc807562c49b8986637f544a4c5e361 Mon Sep 17 00:00:00 2001
From: Paul Spangler <paul.spangler@ni.com>
Date: Wed, 12 Dec 2018 22:34:31 -0600
Subject: [PATCH 4/5] Filter out duplicate test methods

Selects the one declared closest to the test class among all valid test
methods with the same name.
---
 .../Discovery/TypeEnumerator.cs               |  28 ++++-
 .../Discovery/TypeEnumeratorTests.cs          | 105 ++++++++++++++++++
 2 files changed, 132 insertions(+), 1 deletion(-)

diff --git a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
index cbc90a9ff1..8e5946af84 100644
--- a/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
+++ b/src/Adapter/MSTest.CoreAdapter/Discovery/TypeEnumerator.cs
@@ -70,6 +70,8 @@ internal virtual ICollection<UnitTestElement> Enumerate(out ICollection<string>
         /// <returns> List of Valid Tests. </returns>
         internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
         {
+            bool foundDuplicateTests = false;
+            var foundTests = new HashSet<string>();
             var tests = new Collection<UnitTestElement>();
 
             // Test class is already valid. Verify methods.
@@ -85,11 +87,35 @@ internal Collection<UnitTestElement> GetTests(ICollection<string> warnings)
 
                 if (this.testMethodValidator.IsValidTestMethod(method, this.type, warnings))
                 {
+                    foundDuplicateTests = foundDuplicateTests || !foundTests.Add(method.Name);
                     tests.Add(this.GetTestFromMethod(method, isMethodDeclaredInTestTypeAssembly, warnings));
                 }
             }
 
-            return tests;
+            if (!foundDuplicateTests)
+            {
+                return tests;
+            }
+
+            // Remove duplicate test methods by taking the first one of each name
+            // that is declared closest to the test class in the hierarchy.
+            var inheritanceDepths = new Dictionary<string, int>();
+            var currentType = this.type;
+            int currentDepth = 0;
+
+            while (currentType != null)
+            {
+                inheritanceDepths[currentType.FullName] = currentDepth;
+                ++currentDepth;
+                currentType = currentType.GetTypeInfo().BaseType;
+            }
+
+            return new Collection<UnitTestElement>(
+                tests.GroupBy(
+                    t => t.TestMethod.Name,
+                    (_, elements) =>
+                        elements.OrderBy(t => inheritanceDepths[t.TestMethod.DeclaringClassFullName ?? t.TestMethod.FullClassName]).First())
+                    .ToList());
         }
 
         /// <summary>
diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
index fed5311e8d..95d1ccf5bd 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Discovery/TypeEnumeratorTests.cs
@@ -183,6 +183,82 @@ public void GetTestsShouldNotReturnBaseTestMethodsFromAnotherAssemblyByConfigura
                 "DummyDerivedFromRemoteTestClass inherits DummyRemoteBaseTestClass from different assembly. BestTestMethod from DummyRemoteBaseTestClass should not be discovered when RunSettings MSTestV2 specifies EnableBaseClassTestMethodsFromOtherAssemblies = false.");
         }
 
+        [TestMethod]
+        public void GetTestsShouldNotReturnHiddenTestMethods()
+        {
+            this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true);
+            TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DummyHidingTestClass), Assembly.GetExecutingAssembly().FullName);
+
+            var tests = typeEnumerator.Enumerate(out this.warnings);
+
+            Assert.IsNotNull(tests);
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "BaseTestMethod"),
+                "DummyHidingTestClass declares BaseTestMethod directly so it should always be discovered.");
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "DerivedTestMethod"),
+                "DummyHidingTestClass declares BaseTestMethod directly so it should always be discovered.");
+            Assert.IsFalse(
+                tests.Any(t => t.TestMethod.DeclaringClassFullName == typeof(DummyBaseTestClass).FullName),
+                "DummyHidingTestClass hides BaseTestMethod so declaring class should not be the base class");
+        }
+
+        [TestMethod]
+        public void GetTestsShouldReturnOverriddenTestMethods()
+        {
+            this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true);
+            TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DummyOverridingTestClass), Assembly.GetExecutingAssembly().FullName);
+
+            var tests = typeEnumerator.Enumerate(out this.warnings);
+
+            Assert.IsNotNull(tests);
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "BaseTestMethod"),
+                "DummyOverridingTestClass inherits BaseTestMethod so it should be discovered.");
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "DerivedTestMethod"),
+                "DummyOverridingTestClass overrides DerivedTestMethod directly so it should always be discovered.");
+            Assert.AreEqual(
+                typeof(DummyHidingTestClass).FullName,
+                tests.Single(t => t.TestMethod.Name == "BaseTestMethod").TestMethod.DeclaringClassFullName,
+                "DummyOverridingTestClass inherits BaseTestMethod from DummyHidingTestClass specifically.");
+            Assert.IsNull(
+                tests.Single(t => t.TestMethod.Name == "DerivedTestMethod").TestMethod.DeclaringClassFullName,
+                "DummyOverridingTestClass overrides DerivedTestMethod so is the declaring class.");
+        }
+
+        [TestMethod]
+        public void GetTestsShouldNotReturnHiddenTestMethodsFromAnyLevel()
+        {
+            this.SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true, isMethodFromSameAssembly: true);
+            TypeEnumerator typeEnumerator = this.GetTypeEnumeratorInstance(typeof(DummySecondHidingTestClass), Assembly.GetExecutingAssembly().FullName);
+
+            var tests = typeEnumerator.Enumerate(out this.warnings);
+
+            Assert.IsNotNull(tests);
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "BaseTestMethod"),
+                "DummySecondHidingTestClass hides BaseTestMethod so it should be discovered.");
+            Assert.AreEqual(
+                1,
+                tests.Count(t => t.TestMethod.Name == "DerivedTestMethod"),
+                "DummySecondHidingTestClass hides DerivedTestMethod so it should be discovered.");
+            Assert.IsFalse(
+                tests.Any(t => t.TestMethod.DeclaringClassFullName == typeof(DummyBaseTestClass).FullName),
+                "DummySecondHidingTestClass hides all base test methods so declaring class should not be any base class");
+            Assert.IsFalse(
+                tests.Any(t => t.TestMethod.DeclaringClassFullName == typeof(DummyHidingTestClass).FullName),
+                "DummySecondHidingTestClass hides all base test methods so declaring class should not be any base class");
+            Assert.IsFalse(
+                tests.Any(t => t.TestMethod.DeclaringClassFullName == typeof(DummyOverridingTestClass).FullName),
+                "DummySecondHidingTestClass hides all base test methods so declaring class should not be any base class");
+        }
+
         #endregion
 
         #region GetTestFromMethod tests
@@ -444,5 +520,34 @@ public void DerivedTestMethod()
         }
     }
 
+    public class DummyHidingTestClass : DummyBaseTestClass
+    {
+        public new virtual void BaseTestMethod()
+        {
+        }
+
+        public virtual void DerivedTestMethod()
+        {
+        }
+    }
+
+    public class DummyOverridingTestClass : DummyHidingTestClass
+    {
+        public override void DerivedTestMethod()
+        {
+        }
+    }
+
+    public class DummySecondHidingTestClass : DummyOverridingTestClass
+    {
+        public new void BaseTestMethod()
+        {
+        }
+
+        public new void DerivedTestMethod()
+        {
+        }
+    }
+
     #endregion
 }

From a09b1444db4623cf361d2fb89ac56600acbdb4b6 Mon Sep 17 00:00:00 2001
From: Paul Spangler <paul.spangler@ni.com>
Date: Thu, 10 Jan 2019 22:30:37 -0600
Subject: [PATCH 5/5] Fix compile after latest merge from master

---
 .../Helpers/ReflectHelperTests.cs                      | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
index 81c7222453..49bb940e5f 100644
--- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
+++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/ReflectHelperTests.cs
@@ -63,7 +63,7 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtClassLevel()
         }
 
         /// <summary>
-        /// Testing test category attributes adorned at calss, assembly and method level are getting collected.
+        /// Testing test category attributes adorned at class, assembly and method level are getting collected.
         /// </summary>
         [TestMethod]
         public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels()
@@ -72,15 +72,15 @@ public void GetTestCategoryAttributeShouldIncludeTestCategoriesAtAllLevels()
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("AsmLevel3") }, MemberTypes.All);
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("ClassLevel") }, MemberTypes.TypeInfo);
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel") }, MemberTypes.Method);
-          
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
             string[] expected = new[] { "MethodLevel", "ClassLevel", "AsmLevel1", "AsmLevel2", "AsmLevel3" };
 
             CollectionAssert.AreEqual(expected, actual);
         }
 
         /// <summary>
-        /// Testing test category attributes adorned at calss, assembly and method level are getting collected.
+        /// Testing test category attributes adorned at class, assembly and method level are getting collected.
         /// </summary>
         [TestMethod]
         public void GetTestCategoryAttributeShouldConcatCustomAttributeOfSameType()
@@ -92,7 +92,7 @@ public void GetTestCategoryAttributeShouldConcatCustomAttributeOfSameType()
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel1") }, MemberTypes.Method);
             this.reflectHelper.SetCustomAttribute(typeof(UTF.TestCategoryBaseAttribute), new[] { new UTF.TestCategoryAttribute("MethodLevel2") }, MemberTypes.Method);
 
-            var actual = this.reflectHelper.GetCategories(this.method.Object).ToArray();
+            var actual = this.reflectHelper.GetCategories(this.method.Object, typeof(ReflectHelperTests)).ToArray();
             string[] expected = new[] { "MethodLevel1", "MethodLevel2", "ClassLevel1", "ClassLevel2", "AsmLevel1", "AsmLevel2" };
 
             CollectionAssert.AreEqual(expected, actual);
