Skip to content

Commit cf8c602

Browse files
committed
Test covariant returns a little more thoroughly
1 parent 36c608a commit cf8c602

File tree

1 file changed

+122
-0
lines changed

1 file changed

+122
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright 2004-2022 Castle Project - http://www.castleproject.org/
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#if NET5_0_OR_GREATER
16+
17+
using System;
18+
using System.Reflection;
19+
20+
using NUnit.Framework;
21+
22+
namespace Castle.DynamicProxy.Tests
23+
{
24+
[TestFixture]
25+
public class CovariantReturnsTestCase : BasePEVerifyTestCase
26+
{
27+
// DynamicProxy's current implementation for covariant returns support expects to see override methods
28+
// before the overridden methods. That is, we rely on a specific behavior of .NET Reflection, and this test
29+
// codifies that assumption. If it ever breaks, we'll need to adjust our implementation accordingly.
30+
[Test]
31+
public void Reflection_returns_methods_from_a_derived_class_before_methods_from_its_base_class()
32+
{
33+
var derivedType = typeof(DerivedClassWithStringInsteadOfObject);
34+
var baseType = typeof(BaseClassWithObject);
35+
Assume.That(derivedType.BaseType == baseType);
36+
37+
var derivedMethod = derivedType.GetMethod("Method");
38+
var baseMethod = baseType.GetMethod("Method");
39+
Assume.That(derivedMethod != baseMethod);
40+
41+
var methods = derivedType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
42+
var derivedMethodIndex = Array.FindIndex(methods, m => m.Name == "Method" && m.DeclaringType == derivedType);
43+
var baseMethodIndex = Array.FindIndex(methods, m => m.Name == "Method" && m.DeclaringType == baseType);
44+
Assume.That(derivedMethodIndex >= 0);
45+
Assume.That(baseMethodIndex >= 0);
46+
47+
Assert.IsTrue(derivedMethodIndex < baseMethodIndex);
48+
}
49+
50+
[Theory]
51+
[TestCase(typeof(DerivedClassWithInterfaceInsteadOfObject))]
52+
[TestCase(typeof(DerivedClassWithStringInsteadOfObject))]
53+
[TestCase(typeof(DerivedClassWithStringInsteadOfInterface))]
54+
[TestCase(typeof(DerivedClassWithStringInsteadOfGenericArg))]
55+
public void Can_proxy_type_having_method_with_covariant_return(Type classToProxy)
56+
{
57+
_ = generator.CreateClassProxy(classToProxy, new StandardInterceptor());
58+
}
59+
60+
[Theory]
61+
[TestCase(typeof(DerivedClassWithInterfaceInsteadOfObject), typeof(IComparable))]
62+
[TestCase(typeof(DerivedClassWithStringInsteadOfObject), typeof(string))]
63+
[TestCase(typeof(DerivedClassWithStringInsteadOfInterface), typeof(string))]
64+
[TestCase(typeof(DerivedClassWithStringInsteadOfGenericArg), typeof(string))]
65+
public void Proxied_method_has_correct_return_type(Type classToProxy, Type expectedMethodReturnType)
66+
{
67+
var proxy = generator.CreateClassProxy(classToProxy, new StandardInterceptor());
68+
var method = proxy.GetType().GetMethod("Method");
69+
Assert.AreEqual(expectedMethodReturnType, method.ReturnType);
70+
}
71+
72+
[Theory]
73+
[TestCase(typeof(DerivedClassWithInterfaceInsteadOfObject))]
74+
[TestCase(typeof(DerivedClassWithStringInsteadOfObject))]
75+
[TestCase(typeof(DerivedClassWithStringInsteadOfInterface))]
76+
[TestCase(typeof(DerivedClassWithStringInsteadOfGenericArg))]
77+
public void Can_invoke_method_with_covariant_return(Type classToProxy)
78+
{
79+
var proxy = generator.CreateClassProxy(classToProxy, new StandardInterceptor());
80+
var method = proxy.GetType().GetMethod("Method");
81+
var returnValue = method.Invoke(proxy, null);
82+
Assert.AreEqual(expected: classToProxy.Name, returnValue);
83+
}
84+
85+
public class BaseClassWithObject
86+
{
87+
public virtual object Method() => nameof(BaseClassWithObject);
88+
}
89+
90+
public class DerivedClassWithInterfaceInsteadOfObject : BaseClassWithObject
91+
{
92+
public override IComparable Method() => nameof(DerivedClassWithInterfaceInsteadOfObject);
93+
}
94+
95+
public class DerivedClassWithStringInsteadOfObject : BaseClassWithObject
96+
{
97+
public override string Method() => nameof(DerivedClassWithStringInsteadOfObject);
98+
}
99+
100+
public class BaseClassWithInterface
101+
{
102+
public virtual IComparable Method() => nameof(BaseClassWithInterface);
103+
}
104+
105+
public class DerivedClassWithStringInsteadOfInterface : BaseClassWithInterface
106+
{
107+
public override string Method() => nameof(DerivedClassWithStringInsteadOfInterface);
108+
}
109+
110+
public class BaseClassWithGenericArg<T> where T : IComparable
111+
{
112+
public virtual T Method() => default(T);
113+
}
114+
115+
public class DerivedClassWithStringInsteadOfGenericArg : BaseClassWithGenericArg<IComparable>
116+
{
117+
public override string Method() => nameof(DerivedClassWithStringInsteadOfGenericArg);
118+
}
119+
}
120+
}
121+
122+
#endif

0 commit comments

Comments
 (0)