// Copyright 2004-2008 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 System.Reflection.Emit; using System.Collections; using Castle.Core.Interceptor; using Castle.DynamicProxy.Generators; using Castle.DynamicProxy.Tests.BugsReported; using Castle.DynamicProxy.Tests.Classes; using Castle.DynamicProxy.Tests.Interceptors; using Castle.DynamicProxy.Tests.InterClasses; using NUnit.Framework; using ClassWithIndexer=Castle.DynamicProxy.Tests.Classes.ClassWithIndexer; using Castle.DynamicProxy.Generators.Emitters; [TestFixture] public class BasicClassProxyTestCase : BasePEVerifyTestCase { [Test] public void ProxyForClass() { object proxy = generator.CreateClassProxy(typeof (ServiceClass), new ResultModifierInterceptor()); Assert.IsNotNull(proxy); Assert.IsTrue(typeof (ServiceClass).IsAssignableFrom(proxy.GetType())); ServiceClass instance = (ServiceClass) proxy; // return value is changed by the interceptor Assert.AreEqual(44, instance.Sum(20, 25)); // return value is changed by the interceptor Assert.AreEqual(true, instance.Valid); // The rest aren't changed Assert.AreEqual(45, instance.Sum((byte) 20, (byte) 25)); // byte Assert.AreEqual(45, instance.Sum(20L, 25L)); // long Assert.AreEqual(45, instance.Sum((short) 20, (short) 25)); // short Assert.AreEqual(45, instance.Sum(20f, 25f)); // float Assert.AreEqual(45, instance.Sum(20.0, 25.0)); // double Assert.AreEqual(45, instance.Sum((ushort) 20, (ushort) 25)); // ushort Assert.AreEqual(45, instance.Sum((uint) 20, (uint) 25)); // uint Assert.AreEqual(45, instance.Sum((ulong) 20, (ulong) 25)); // ulong } [Test] public void Caching() { object proxy = generator.CreateClassProxy( typeof (ServiceClass), new StandardInterceptor()); proxy = generator.CreateClassProxy( typeof (ServiceClass), new StandardInterceptor()); proxy = generator.CreateClassProxy( typeof (ServiceClass), new StandardInterceptor()); proxy = generator.CreateClassProxy( typeof (ServiceClass), new StandardInterceptor()); } #if !MONO [Test, ExpectedException(typeof (GeneratorException), ExpectedMessage = "Type is not public, so a proxy " + "cannot be generated. Type: System.AppDomainInitializerInfo" )] public void ProxyForNonPublicClass() { // have to use a type that is not from this assembly, because it is marked as internals visible to // DynamicProxy2 object proxy = generator.CreateClassProxy( Type.GetType("System.AppDomainInitializerInfo, mscorlib"), new StandardInterceptor()); } #endif [Test] public void ProxyForClassWithIndexer() { LogInvocationInterceptor logger = new LogInvocationInterceptor(); object proxy = generator.CreateClassProxy(typeof (ClassWithIndexer), logger); Assert.IsNotNull(proxy); Assert.IsInstanceOfType(typeof (ClassWithIndexer), proxy); ClassWithIndexer type = (ClassWithIndexer) proxy; type["name"] = 10; Assert.AreEqual(10, type["name"]); Assert.AreEqual("set_Item get_Item ", logger.LogContents); } #if !MONO [Test] public void ClassWithDifferentAccessLevelOnProperties() { LogInvocationInterceptor logger = new LogInvocationInterceptor(); object proxy = generator.CreateClassProxy(typeof (DiffAccessLevelOnProperties), logger); Assert.IsNotNull(proxy); Assert.IsInstanceOfType(typeof (DiffAccessLevelOnProperties), proxy); DiffAccessLevelOnProperties type = (DiffAccessLevelOnProperties) proxy; type.SetProperties(); Assert.AreEqual("10 11 12 13 name", type.ToString()); } #endif [Test] public void GetPropertyByReflectionTest() { object proxy = generator.CreateClassProxy( typeof (ServiceClass), new StandardInterceptor()); try { Assert.IsFalse((bool) proxy.GetType().GetProperty("Valid").GetValue(proxy, null), "check reflected property is true"); } catch (AmbiguousMatchException) { // this exception is acceptible if the current runtime doesn't // have .NET 2.0 SP1 installed // we'd try to grab a method info that in in .NET 2.0 SP1, and if it's // not present then we'd ignore that exception MethodInfo newDefinePropertyMethodInfo = typeof (TypeBuilder).GetMethod("DefineProperty", new Type[] { typeof (string), typeof ( PropertyAttributes), typeof ( CallingConventions), typeof (Type), typeof (Type[]), typeof (Type[]), typeof (Type[]), typeof (Type[][]), typeof (Type[][]) }); bool net20SP1IsInstalled = newDefinePropertyMethodInfo != null; if (net20SP1IsInstalled) throw; } } [Test] public void ClassWithInheritance() { LogInvocationInterceptor logger = new LogInvocationInterceptor(); object proxy = generator.CreateClassProxy(typeof (ExtendedServiceClass), logger); Assert.IsNotNull(proxy); ExtendedServiceClass extended = (ExtendedServiceClass) proxy; extended.Sum2(1, 2); extended.Sum(1, 2); Assert.AreEqual("Sum2 Sum ", logger.LogContents); } [Test] public void ProxyForNestedClass() { object proxy = generator.CreateClassProxy(typeof (ServiceClass.InernalClass), new Type[] {typeof (IDisposable)}); Assert.IsNotNull(proxy); Assert.IsTrue(proxy is ServiceClass.InernalClass); } [Test] public void ProxyForClassWithInterfaces() { object proxy = generator.CreateClassProxy(typeof (ServiceClass), new Type[] {typeof (IDisposable)}, new ResultModifierInterceptor()); Assert.IsNotNull(proxy); Assert.IsTrue(typeof (ServiceClass).IsAssignableFrom(proxy.GetType())); Assert.IsTrue(typeof (IDisposable).IsAssignableFrom(proxy.GetType())); ServiceClass inter = (ServiceClass) proxy; Assert.AreEqual(44, inter.Sum(20, 25)); Assert.AreEqual(true, inter.Valid); try { IDisposable disp = (IDisposable) proxy; disp.Dispose(); Assert.Fail("Expected exception as Dispose has no implementation"); } catch (NotImplementedException ex) { Assert.AreEqual("This is a DynamicProxy2 error: the interceptor attempted " + "to 'Proceed' for a method without a target, for example, an interface method or an abstract method", ex.Message); } } [Test] public void ProxyForCharReturnType() { LogInvocationInterceptor logger = new LogInvocationInterceptor(); object proxy = generator.CreateClassProxy(typeof (ClassWithCharRetType), logger); Assert.IsNotNull(proxy); ClassWithCharRetType classProxy = (ClassWithCharRetType) proxy; Assert.AreEqual('c', classProxy.DoSomething()); } [Test] public void ProxyForClassWithConstructors() { object proxy = generator.CreateClassProxy( typeof (ClassWithConstructors), new IInterceptor[] {new StandardInterceptor()}, new object[] {"name"}); Assert.IsNotNull(proxy); ClassWithConstructors classProxy = (ClassWithConstructors) proxy; Assert.AreEqual("name", classProxy.Name); proxy = generator.CreateClassProxy( typeof (ClassWithConstructors), new IInterceptor[] {new StandardInterceptor()}, new object[] {"name", 10}); Assert.IsNotNull(proxy); classProxy = (ClassWithConstructors) proxy; Assert.AreEqual("name", classProxy.Name); Assert.AreEqual(10, classProxy.X); } /// /// See http://support.castleproject.org/browse/DYNPROXY-43 /// [Test] public void MethodParamNamesAreReplicated() { MyClass mc = generator.CreateClassProxy(new StandardInterceptor()); ParameterInfo[] methodParams = GetMyTestMethodParams(mc.GetType()); Assert.AreEqual("myParam", methodParams[0].Name); } [Test] public void ProducesInvocationsThatCantChangeTarget() { AssertCannotChangeTargetInterceptor invocationChecker = new AssertCannotChangeTargetInterceptor(); object proxy = generator.CreateClassProxy(typeof (ClassWithCharRetType), invocationChecker); Assert.IsNotNull(proxy); ClassWithCharRetType classProxy = (ClassWithCharRetType) proxy; Assert.AreEqual('c', classProxy.DoSomething()); } [Test] [Ignore("Multi dimensional arrays seems to not work at all")] public void ProxyTypeWithMultiDimentionalArrayAsParameters() { LogInvocationInterceptor log = new LogInvocationInterceptor(); ClassWithMultiDimentionalArray proxy = generator.CreateClassProxy(log); int[,] x = new int[1,2]; proxy.Do(new int[] {1}); proxy.Do2(x); proxy.Do3(new string[] {"1", "2"}); Assert.AreEqual("Do Do2 Do3 ", log.LogContents); } private ParameterInfo[] GetMyTestMethodParams(Type type) { MethodInfo methodInfo = type.GetMethod("MyTestMethod"); return methodInfo.GetParameters(); } [Test] public void ProxyForBaseTypeFromSignedAssembly() { Type t = typeof (Hashtable); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(t.Assembly)); object proxy = generator.CreateClassProxy(t, new StandardInterceptor()); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] public void ProxyForBaseTypeAndInterfaceFromSignedAssembly() { Type t1 = typeof (Hashtable); Type t2 = typeof (IServiceProvider); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(t1.Assembly)); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(t2.Assembly)); object proxy = generator.CreateClassProxy(t1, new Type[] {t2}, new StandardInterceptor()); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] [Ignore("To get this running, the Tests project must not be signed.")] public void ProxyForBaseTypeFromUnsignedAssembly() { Type t = typeof (MyClass); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(t.Assembly)); object proxy = generator.CreateClassProxy(t, new StandardInterceptor()); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] [Ignore("To get this running, the Tests project must not be signed.")] public void ProxyForBaseTypeAndInterfaceFromUnsignedAssembly() { Type t1 = typeof (MyClass); Type t2 = typeof (IService); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(t1.Assembly)); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(t2.Assembly)); object proxy = generator.CreateClassProxy(t1, new Type[] {t2}, new StandardInterceptor()); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] [Ignore("To get this running, the Tests project must not be signed.")] public void ProxyForBaseTypeAndInterfaceFromSignedAndUnsignedAssemblies1() { Type t1 = typeof (MyClass); Type t2 = typeof (IServiceProvider); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(t1.Assembly)); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(t2.Assembly)); object proxy = generator.CreateClassProxy(t1, new Type[] {t2}, new StandardInterceptor()); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] [Ignore("To get this running, the Tests project must not be signed.")] public void ProxyForBaseTypeAndInterfaceFromSignedAndUnsignedAssemblies2() { Type t1 = typeof (Hashtable); Type t2 = typeof (IService); Assert.IsTrue(StrongNameUtil.IsAssemblySigned(t1.Assembly)); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(t2.Assembly)); object proxy = generator.CreateClassProxy(t1, new Type[] {t2}, new StandardInterceptor()); Assert.IsFalse(StrongNameUtil.IsAssemblySigned(proxy.GetType().Assembly)); } [Test] public void VirtualCallFromCtor() { StandardInterceptor interceptor = new StandardInterceptor(); ClassCallingVirtualMethodFromCtor proxy = generator.CreateClassProxy(interceptor); Assert.AreEqual(7, proxy.Result); } } }