Skip to content

Commit

Permalink
Merge pull request #436 from stakx/delegate-mixins
Browse files Browse the repository at this point in the history
Add ability to mix in delegate (types) to proxies
  • Loading branch information
stakx committed Apr 2, 2019
2 parents 0f5bdb7 + 4130cbc commit b066111
Show file tree
Hide file tree
Showing 13 changed files with 725 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,7 @@ Enhancements:
- Added trace logging level below Debug; maps to Trace in log4net/NLog, and Verbose in Serilog (@pi3k14, #404)
- Recognize read-only parameters by the `In` modreq (@zvirja, #406)
- DictionaryAdapter: Exposed GetAdapter overloads with NameValueCollection parameter in .NET Standard (@rzontar, #423)
- Ability to add delegate mixins to proxies using `ProxyGenerationOptions.AddDelegate[Type]Mixin`. You can bind to the mixed-in `Invoke` methods on the proxy using `ProxyUtil.CreateDelegateToMixin`. (@stakx, #436)
- New `IInvocation.CaptureProceedInfo()` method to enable better implementations of asynchronous interceptors (@stakx, #439)

Deprecations:
Expand Down
311 changes: 311 additions & 0 deletions src/Castle.Core.Tests/DynamicProxy.Tests/DelegateMixinTestCase.cs
@@ -0,0 +1,311 @@
// Copyright 2004-2010 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.DynamicProxy.Tests
{
using System;
using System.Reflection;

using NUnit.Framework;

[TestFixture]
public class DelegateMixinTestCase : BasePEVerifyTestCase
{
[Test]
public void ProxyGenerationOptions_AddDelegateTypeMixin_when_given_null_throws_ArgumentNullException()
{
var options = new ProxyGenerationOptions();
Assert.Throws<ArgumentNullException>(() => options.AddDelegateTypeMixin(null));
}

[Test]
public void ProxyGenerationOptions_AddDelegateTypeMixin_when_given_non_delegate_type_throws_ArgumentException()
{
var options = new ProxyGenerationOptions();
Assert.Throws<ArgumentException>(() => options.AddDelegateTypeMixin(typeof(Exception)));
}

[Test]
public void ProxyGenerationOptions_AddDelegateTypeMixin_when_given_delegate_type_succeeds()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));
}

[Test]
public void ProxyGenerationOptions_AddDelegateMixin_when_given_null_throws_ArgumentNullException()
{
var options = new ProxyGenerationOptions();
Assert.Throws<ArgumentNullException>(() => options.AddDelegateMixin(null));
}

[Test]
public void ProxyGenerationOptions_AddDelegateMixin_when_given_delegate_succeeds()
{
var options = new ProxyGenerationOptions();
options.AddDelegateMixin(new Action(() => { }));
}


[Test]
public void ProxyGenerator_CreateClassProxy_can_create_delegate_proxy_without_target()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));
var _ = new Interceptor();
var proxy = generator.CreateClassProxy(typeof(object), options, _);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithoutTarget_can_create_delegate_proxy_without_target()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));
var _ = new Interceptor();
var proxy = generator.CreateInterfaceProxyWithoutTarget(typeof(IComparable), options, _);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithTarget_can_create_delegate_proxy_without_target()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));
var _ = new Interceptor();
var proxy = generator.CreateInterfaceProxyWithTarget(typeof(IComparable), target, options, _);
}

[Test]
public void ProxyGenerator_CreateClassProxy_can_create_callable_delegate_proxy_without_target()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor();

var proxy = generator.CreateClassProxy(typeof(object), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.AreSame(typeof(Action).GetTypeInfo().GetMethod("Invoke"), interceptor.LastInvocation.Method);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithoutTarget_can_create_callable_delegate_proxy_without_target()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor();

var proxy = generator.CreateInterfaceProxyWithoutTarget(typeof(IComparable), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.AreSame(typeof(Action).GetTypeInfo().GetMethod("Invoke"), interceptor.LastInvocation.Method);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithTarget_can_create_callable_delegate_proxy_without_target()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor();

var proxy = generator.CreateInterfaceProxyWithTarget(typeof(IComparable), target, options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.AreSame(typeof(Action).GetTypeInfo().GetMethod("Invoke"), interceptor.LastInvocation.Method);
}

[Test]
public void ProxyGenerator_CreateClassProxy_cannot_proceed_to_delegate_type_mixin()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateClassProxy(typeof(object), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

Assert.Throws<NotImplementedException>(() => action.Invoke());
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithoutTarget_cannot_proceed_to_delegate_type_mixin()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateInterfaceProxyWithoutTarget(typeof(IComparable), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

Assert.Throws<NotImplementedException>(() => action.Invoke());
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithTarget_cannot_proceed_to_delegate_type_mixin()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateInterfaceProxyWithTarget(typeof(IComparable), target, options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

Assert.Throws<NotImplementedException>(() => action.Invoke());
}

[Test]
public void ProxyGenerator_CreateClassProxy_can_proceed_to_delegate_mixin()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateMixin(new Action(target.Method));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateClassProxy(typeof(object), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.True(target.MethodInvoked);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithoutTarget_can_proceed_to_delegate_mixin()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateMixin(new Action(target.Method));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateInterfaceProxyWithoutTarget(typeof(IComparable), options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.True(target.MethodInvoked);
}

[Test]
public void ProxyGenerator_CreateInterfaceProxyWithTarget_can_proceed_to_delegate_mixin()
{
var target = new Target();

var options = new ProxyGenerationOptions();
options.AddDelegateMixin(new Action(target.Method));

var interceptor = new Interceptor(shouldProceed: true);

var proxy = generator.CreateInterfaceProxyWithTarget(typeof(IComparable), target, options, interceptor);
var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);

action.Invoke();
Assert.True(target.MethodInvoked);
}

[Test]
public void Can_mixin_several_different_delegate_types_simultaneously()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Action));
options.AddDelegateTypeMixin(typeof(Action<int>));

var interceptor = new Interceptor();

var proxy = generator.CreateClassProxy(typeof(object), options, interceptor);

var action = ProxyUtil.CreateDelegateToMixin<Action>(proxy);
Assert.NotNull(action);
action.Invoke();

var intAction = ProxyUtil.CreateDelegateToMixin<Action<int>>(proxy);
Assert.NotNull(action);
intAction.Invoke(42);
}

[Test]
public void Cannot_mixin_several_delegate_types_with_same_signature()
{
var options = new ProxyGenerationOptions();
options.AddDelegateTypeMixin(typeof(Func<Exception, bool>));
options.AddDelegateTypeMixin(typeof(Predicate<Exception>));
Assert.Throws<InvalidMixinConfigurationException>(() => options.Initialize());

}

[Serializable]
public sealed class Target : IComparable
{
public bool CompareToInvoked { get; set; }

public bool MethodInvoked { get; set; }

public int CompareTo(object obj)
{
CompareToInvoked = true;
return 123;
}

public void Method()
{
MethodInvoked = true;
}
}

private sealed class Interceptor : IInterceptor
{
private readonly bool shouldProceed;

public Interceptor(bool shouldProceed = false)
{
this.shouldProceed = shouldProceed;
}

public IInvocation LastInvocation { get; set; }

public void Intercept(IInvocation invocation)
{
LastInvocation = invocation;
if (shouldProceed)
{
invocation.Proceed();
}
}
}
}
}

0 comments on commit b066111

Please sign in to comment.