You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/05/16 20:50:31 UTC
[1/44] git commit: Convert TestNG to Spock
Updated Branches:
refs/heads/master a84ac7bc2 -> 6bd952865
Convert TestNG to Spock
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/a824b8df
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/a824b8df
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/a824b8df
Branch: refs/heads/master
Commit: a824b8df6215aee64ad4dc86db1c8153f966a370
Parents: 63ad333
Author: Howard M. Lewis Ship <hl...@gmail.com>
Authored: Tue May 15 10:08:09 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed May 16 11:50:15 2012 -0700
----------------------------------------------------------------------
.../ioc/internal/DefaultModuleDefImpl.java | 33 +-
.../apache/tapestry5/ioc/internal/IOCMessages.java | 14 +-
.../tapestry5/ioc/internal/IOCStrings.properties | 6 +-
.../ioc/internal/DefaultModuleDefImplSpec.groovy | 449 ++++++++++
.../ioc/internal/ComplexAutobuildModule.java | 4 +-
.../ioc/internal/DefaultModuleDefImplTest.java | 679 ---------------
6 files changed, 476 insertions(+), 709 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
index 0bae285..f2f408b 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -90,9 +90,12 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
}
/**
- * @param moduleClass the class that is responsible for building services, etc.
- * @param logger based on the class name of the module
- * @param proxyFactory factory used to create proxy classes at runtime
+ * @param moduleClass
+ * the class that is responsible for building services, etc.
+ * @param logger
+ * based on the class name of the module
+ * @param proxyFactory
+ * factory used to create proxy classes at runtime
*/
public DefaultModuleDefImpl(Class<?> moduleClass, Logger logger, PlasticProxyFactory proxyFactory)
{
@@ -294,7 +297,13 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
Class returnType = method.getReturnType();
if (returnType.isPrimitive() || returnType.isArray())
- throw new RuntimeException(IOCMessages.decoratorMethodWrongReturnType(method));
+ {
+ throw new RuntimeException(String.format(
+ "Method %s is named like a service decorator method, but the return type (%s) is not acceptable (try Object).",
+ InternalUtils.asString(method),
+ method.getReturnType().getCanonicalName()));
+ }
+
Set<Class> markers = extractMarkers(method, Decorate.class);
@@ -409,7 +418,10 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
Class returnType = method.getReturnType();
if (returnType.isPrimitive() || returnType.isArray())
- throw new RuntimeException(IOCMessages.buildMethodWrongReturnType(method));
+ throw new RuntimeException(
+ String.format("Method %s is named like a service builder method, but the return type (%s) is not acceptable (try an interface).",
+ InternalUtils.asString(method),
+ method.getReturnType().getCanonicalName()));
String scope = extractServiceScope(method);
boolean eagerLoad = method.isAnnotationPresent(EagerLoad.class);
@@ -513,11 +525,12 @@ public class DefaultModuleDefImpl implements ModuleDef2, ServiceDefAccumulator
/**
* See if the build class defined a bind method and invoke it.
*
- * @param remainingMethods set of methods as yet unaccounted for
+ * @param remainingMethods
+ * set of methods as yet unaccounted for
* @param modulePreventsServiceDecoration
- * true if {@link org.apache.tapestry5.ioc.annotations.PreventServiceDecoration} on
- * module
- * class
+ * true if {@link org.apache.tapestry5.ioc.annotations.PreventServiceDecoration} on
+ * module
+ * class
*/
private void bind(Set<Method> remainingMethods, boolean modulePreventsServiceDecoration)
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
index 385b489..7584702 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/IOCMessages.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -42,18 +42,6 @@ final class IOCMessages
return MESSAGES.format("build-method-conflict", serviceId, conflict, existing);
}
- static String buildMethodWrongReturnType(Method method)
- {
- return MESSAGES.format("build-method-wrong-return-type", asString(method), method
- .getReturnType().getCanonicalName());
- }
-
- static String decoratorMethodWrongReturnType(Method method)
- {
- return MESSAGES.format("decorator-method-wrong-return-type", asString(method), method
- .getReturnType().getCanonicalName());
- }
-
static String serviceWrongInterface(String serviceId, Class actualInterface, Class requestedInterface)
{
return MESSAGES.format("service-wrong-interface", serviceId, actualInterface.getName(),
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties b/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
index 066e9d5..73fe7e7 100644
--- a/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
+++ b/tapestry-ioc/src/main/resources/org/apache/tapestry5/ioc/internal/IOCStrings.properties
@@ -1,4 +1,4 @@
- # Copyright 2006, 2007, 2008, 2009, 2011 The Apache Software Foundation
+ # Copyright 2006, 2007, 2008, 2009, 2011, 2012 The Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,10 +13,6 @@
# limitations under the License.
build-method-conflict=Service %s (defined by %s) conflicts with previously defined service defined by %s.
-build-method-wrong-return-type=Method %s is named like a service builder method, \
- but the return type (%s) is not acceptable (try an interface).
-decorator-method-wrong-return-type=Method %s is named like a service decorator method, \
- but the return type (%s) is not acceptable (try Object).
service-wrong-interface=Service '%s' implements interface %s, which is not compatible with the requested type %s.
instantiate-builder-error=Unable to instantiate class %s as a module: %s
no-service-matches-type=No service implements the interface %s.
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplSpec.groovy
new file mode 100644
index 0000000..c79ad3a
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplSpec.groovy
@@ -0,0 +1,449 @@
+package org.apache.tapestry5.ioc.internal
+
+import org.apache.tapestry5.internal.plastic.PlasticClassLoader
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils
+import org.apache.tapestry5.internal.plastic.asm.ClassWriter
+import org.apache.tapestry5.ioc.def.ServiceDef3
+import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory
+import org.slf4j.Logger
+import spock.lang.Shared
+import spock.lang.Specification
+import spock.lang.Unroll
+import org.apache.tapestry5.ioc.*
+
+import static org.apache.tapestry5.internal.plastic.asm.Opcodes.*
+
+class DefaultModuleDefImplSpec extends Specification {
+
+ @Shared
+ PlasticProxyFactory proxyFactory = new PlasticProxyFactoryImpl(Thread.currentThread().contextClassLoader, null)
+
+ @Shared
+ OperationTracker tracker = new QuietOperationTracker()
+
+ Logger logger = Mock()
+
+ def "toString() of module lists services in the module"() {
+ when:
+
+ def md = module SimpleModule
+
+ then:
+
+ md.toString() == "ModuleDef[$SimpleModule.name Barney, Fred, Wilma]"
+ }
+
+ def "serviceIds contains all service ids"() {
+ def md = module SimpleModule
+
+ expect:
+
+ md.serviceIds == ["Fred", "Barney", "Wilma"] as Set
+ }
+
+ def "ServiceDef obtainable by service id"() {
+ def md = module SimpleModule
+
+ when:
+
+ def sd = md.getServiceDef "fred"
+
+ then:
+
+ sd.serviceId == "Fred"
+ sd.serviceInterface == FieService
+ sd.toString().contains "${SimpleModule.name}.buildFred()"
+ sd.serviceScope == ScopeConstants.DEFAULT
+ !sd.eagerLoad
+ sd.markers.empty
+
+ when:
+
+ sd = md.getServiceDef("Wilma")
+
+ then:
+
+ sd.eagerLoad
+ }
+
+ def "ModuleDef exposes decorator methods as DecoratorDefs"() {
+ def md = module SimpleModule
+
+ when:
+
+ def decos = md.decoratorDefs
+
+ then:
+
+ decos.size() == 1
+
+ def deco = decos.find()
+
+ deco.decoratorId == "Logging"
+ deco.toString().contains "${SimpleModule.name}.decorateLogging(Class, Object)"
+ }
+
+ def "@ServiceId annotation on service builder method overrides naming convention"() {
+ when:
+
+ def md = module ServiceIdViaAnnotationModule
+
+ then:
+
+ md.getServiceDef("FooService") != null
+ }
+
+ def "@ServiceId on implementation class overrides default id from ServiceBinder.bind() default"() {
+ when:
+
+ def md = module ServiceIdViaAnnotationModule
+
+ then:
+
+ md.getServiceDef("BarneyService") != null
+ }
+
+ def "@Named annotation on service builder method overrides naming convention"() {
+ when:
+
+ def md = module NamedServiceModule
+
+ then:
+
+ md.getServiceDef("BazService") != null
+ }
+
+ def "@Named annotation on service implementation class overrides ServiceBinder.bind() default"() {
+ when:
+
+ def md = module NamedServiceModule
+
+ then:
+
+ md.getServiceDef("QuuxService") != null
+ }
+
+ def "naming convention for a service builder method named build() is derived from the return type"() {
+ when:
+
+ def md = module DefaultServiceIdModule
+
+ then:
+
+ md.getServiceDef("FieService") != null
+ }
+
+ def "conflicting service ids result in an exception"() {
+ when:
+
+ module ServiceIdConflictMethodModule
+
+ then:
+
+ RuntimeException ex = thrown()
+
+ ex.message.contains "Service Fred (defined by ${ServiceIdConflictMethodModule.name}.buildFred()"
+ ex.message.contains "conflicts with previously defined service defined by ${ServiceIdConflictMethodModule.name}.buildFred(Object)"
+ }
+
+ def "a service builder method may not return void"() {
+ when:
+
+ module VoidBuilderMethodModule
+
+ then:
+
+ RuntimeException ex = thrown()
+
+ ex.message.contains "${VoidBuilderMethodModule.name}.buildNull()"
+ ex.message.contains "but the return type (void) is not acceptable"
+ }
+
+ def "a service builder method may not return an array"() {
+ when:
+
+ module BuilderMethodModule
+
+ then:
+
+ RuntimeException ex = thrown()
+
+ ex.message.contains "${BuilderMethodModule.name}.buildStringArray()"
+ ex.message.contains "but the return type (java.lang.String[])"
+ }
+
+ @Unroll
+ def "A decorator method #desc"() {
+ when:
+
+ module moduleClass
+
+ then:
+
+ RuntimeException e = thrown()
+
+ e.message.contains expectedText
+
+ where:
+
+ moduleClass | expectedText | desc
+ PrimitiveDecoratorMethodModule | "decoratePrimitive" | "may not return a primitive type"
+ ArrayDecoratorMethodModule | "decorateArray" | "may not return an array"
+ }
+
+ @Unroll
+ def "#desc"() {
+ when:
+
+ def md = module moduleClass
+
+ then:
+
+ def defs = md.contributionDefs
+
+ defs.size() == 1
+
+ def cd = defs.find()
+
+ cd.serviceId == serviceId
+
+ cd.toString().contains "${moduleClass.name}.$methodSignature"
+
+ where:
+
+ moduleClass | serviceId | methodSignature | desc
+ SimpleModule | "Barney" | "contributeBarney(Configuration)" | "contribution without annotation to configuration"
+ OrderedConfigurationModule | "Ordered" | "contributeOrdered(OrderedConfiguration)" | "contribution to ordered configuration"
+ MappedConfigurationModule | "Mapped" | "contributeMapped(MappedConfiguration)" | "contribution to mapped configuration"
+ }
+
+ @Unroll
+ def "service contribution method that #desc throws an exception"() {
+
+ when:
+
+ module moduleClass
+
+ then:
+
+ RuntimeException e = thrown()
+
+ e.message.contains message
+
+ where:
+
+ moduleClass | message | desc
+
+ NoUsableContributionParameterModule | "does not contain a parameter of type Configuration, OrderedConfiguration or MappedConfiguration" | "does not include configuration parameter"
+ TooManyContributionParametersModule | "contains more than one parameter of type Configuration, OrderedConfiguration, or MappedConfiguration" | "includes more than one configuration parameter"
+ }
+
+ def "using defaults for ServiceBinder.bind()"() {
+
+ when:
+
+ def md = module AutobuildModule
+ ServiceDef3 sd = md.getServiceDef "stringholder"
+
+ then:
+
+ sd.serviceInterface == StringHolder
+ sd.serviceId == "StringHolder"
+ sd.serviceScope == ScopeConstants.DEFAULT
+ !sd.isEagerLoad()
+ sd.markers.empty
+ !sd.preventDecoration
+ }
+
+ def "overriding defaults for ServiceBinder.bind()"() {
+
+ when:
+
+ def md = module ComplexAutobuildModule
+ ServiceDef3 sd = md.getServiceDef "sh"
+
+ then:
+
+ sd.serviceInterface == StringHolder
+ sd.serviceId == "SH"
+ sd.serviceScope == "magic"
+ sd.eagerLoad
+ sd.preventDecoration
+ }
+
+ def "implementation class for ServiceBinder.bind() must have a public constructor"() {
+ when:
+
+ module UninstantiableAutobuildServiceModule
+
+ then:
+
+ RuntimeException e = thrown()
+
+ e.message.contains "Class org.apache.tapestry5.ioc.internal.RunnableServiceImpl (implementation of service 'Runnable') does not contain any public constructors."
+ }
+
+ def "the bind() method of a module class must be a static method"() {
+ when:
+
+ module NonStaticBindMethodModule
+
+ then:
+
+ RuntimeException e = thrown()
+
+ e.message.contains "Method org.apache.tapestry5.ioc.internal.NonStaticBindMethodModule.bind(ServiceBinder)"
+ e.message.contains "appears to be a service binder method, but is an instance method, not a static method"
+ }
+
+ def "when autobuilding a service implementation, the constructor with the most parameters is chosen"() {
+ ServiceBuilderResources resources = Mock()
+
+ when:
+
+ def md = module MutlipleAutobuildServiceConstructorsModule
+
+ def sd = md.getServiceDef "stringholder"
+
+ then:
+
+ sd != null
+
+ 0 * _
+
+ when:
+
+ def oc = sd.createServiceCreator(resources)
+ def holder = oc.createObject()
+
+ holder.value = "foo"
+
+ then:
+
+ holder instanceof StringHolder
+ holder.value == "FOO"
+
+ _ * resources.serviceId >> "StringHolder"
+ _ * resources.logger >> logger
+ _ * resources.serviceInterface >> StringHolder
+ 1 * resources.getService("ToUpperCaseStringHolder", StringHolder) >> new ToUpperCaseStringHolder()
+ _ * resources.tracker >> tracker
+
+ 1 * logger.debug(_) >> { args ->
+ assert args[0].contains(
+ "Invoking constructor org.apache.tapestry5.ioc.internal.MultipleConstructorsAutobuildService(StringHolder)")
+ }
+
+ 0 * _
+ }
+
+ def "an exception inside a bind() method bubbles up"() {
+ when:
+
+ module ExceptionInBindMethod
+
+ then:
+
+ RuntimeException e = thrown()
+
+ e.message.contains "Error invoking service binder method org.apache.tapestry5.ioc.internal.ExceptionInBindMethod.bind(ServiceBinder)"
+ e.message.contains "at ExceptionInBindMethod.java"
+ e.message.contains "Really, how often is this going to happen?"
+ }
+
+ def "@EagerLoad annotation on service implementation class is reflected in the ServiceDef"() {
+ when:
+
+ def md = module EagerLoadViaAnnotationModule
+ def sd = md.getServiceDef "runnable"
+
+ then:
+
+ sd.eagerLoad
+ }
+
+ private DefaultModuleDefImpl module(moduleClass) {
+ new DefaultModuleDefImpl(moduleClass, logger, proxyFactory)
+ }
+
+ def "marker annotations on the service builder method are available in the ServiceDef"() {
+
+ when:
+
+ def md = module MarkerModule
+ def sd = md.getServiceDef "greeter"
+
+ then:
+
+ sd.markers == [BlueMarker] as Set
+ }
+
+ def "marker annotations specified via ServiceBinder is available in the ServiceDef"() {
+ when:
+
+ def md = module MarkerModule
+ def sd = md.getServiceDef "redgreeter"
+
+ then:
+
+ sd.markers == [RedMarker] as Set
+ }
+
+ def "marker annotation on the implementation class is available in the ServiceDef"() {
+ when:
+
+ def md = module MarkerModule
+ def sd = md.getServiceDef "SecondRedGreeter"
+
+ then:
+
+ sd.markers == [RedMarker] as Set
+ }
+
+ def "marker annotation from ServiceBinder and implementation class are merged"() {
+ when:
+
+ def md = module MarkerModule
+ def sd = md.getServiceDef "SurprisinglyBlueGreeter"
+
+ then:
+
+ sd.markers == [RedMarker, BlueMarker] as Set
+ }
+
+ def "public synthetic methods on module class are ignored"() {
+ def moduleClass = createSyntheticModuleClass()
+
+ when:
+
+ def md = module moduleClass
+
+ then:
+
+ md.serviceIds.size() == 1
+ }
+
+ private createSyntheticModuleClass() {
+
+ def cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES)
+
+ cw.visit(V1_5, ACC_PUBLIC, "EnhancedSyntheticMethodModule", null,
+ PlasticInternalUtils.toInternalName(SyntheticMethodModule.name), null);
+
+ def mv = cw.visitMethod ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, "synth", "()V", null, null
+ mv.visitCode()
+ mv.visitInsn RETURN
+ mv.visitEnd()
+
+ cw.visitEnd()
+
+ def bytecode = cw.toByteArray()
+
+ ClassLoader loader = Thread.currentThread().contextClassLoader
+
+ PlasticClassLoader plasticLoader = new PlasticClassLoader(loader, new NoopClassLoaderDelegate())
+
+ return plasticLoader.defineClassWithBytecode("EnhancedSyntheticMethodModule", bytecode)
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/ComplexAutobuildModule.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/ComplexAutobuildModule.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/ComplexAutobuildModule.java
index 0302bdb..cc578fa 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/ComplexAutobuildModule.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/ComplexAutobuildModule.java
@@ -1,4 +1,4 @@
-// Copyright 2007 The Apache Software Foundation
+// Copyright 2007, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -22,6 +22,6 @@ public class ComplexAutobuildModule
{
public static void bind(ServiceBinder binder)
{
- binder.bind(StringHolder.class, StringHolderImpl.class).eagerLoad().scope("magic").withId("SH");
+ binder.bind(StringHolder.class, StringHolderImpl.class).eagerLoad().scope("magic").withId("SH").preventDecoration();
}
}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a824b8df/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplTest.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplTest.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplTest.java
deleted file mode 100644
index 0fff1dc..0000000
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/DefaultModuleDefImplTest.java
+++ /dev/null
@@ -1,679 +0,0 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
-//
-// 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.
-
-package org.apache.tapestry5.ioc.internal;
-
-import org.apache.tapestry5.internal.plastic.PlasticClassLoader;
-import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
-import org.apache.tapestry5.internal.plastic.asm.ClassWriter;
-import org.apache.tapestry5.internal.plastic.asm.MethodVisitor;
-import org.apache.tapestry5.ioc.*;
-import org.apache.tapestry5.ioc.def.ContributionDef;
-import org.apache.tapestry5.ioc.def.DecoratorDef;
-import org.apache.tapestry5.ioc.def.ModuleDef;
-import org.apache.tapestry5.ioc.def.ServiceDef;
-import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl;
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
-import org.apache.tapestry5.ioc.test.IOCTestCase;
-import org.slf4j.Logger;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.lang.reflect.Method;
-import java.util.Set;
-
-import static org.apache.tapestry5.internal.plastic.asm.Opcodes.*;
-import static org.easymock.EasyMock.contains;
-
-public class DefaultModuleDefImplTest extends IOCTestCase
-{
- private PlasticProxyFactory proxyFactory;
-
- private final OperationTracker tracker = new QuietOperationTracker();
-
- @BeforeClass
- public void setup()
- {
- proxyFactory = new PlasticProxyFactoryImpl(Thread.currentThread().getContextClassLoader(), null);
- }
-
- @AfterClass
- public void cleanup()
- {
- proxyFactory = null;
- }
-
- @Test
- public void simple_module() throws Exception
- {
- String className = SimpleModule.class.getName();
-
- Logger logger = mockLogger();
-
- replay();
-
- // BigDecimal is arbitrary, any class would do.
-
- ModuleDef md = new DefaultModuleDefImpl(SimpleModule.class, logger, proxyFactory);
-
- assertEquals(md.toString(), "ModuleDef[" + className + " Barney, Fred, Wilma]");
-
- Set<String> ids = md.getServiceIds();
-
- assertEquals(ids.size(), 3);
- assertTrue(ids.contains("Fred"));
- assertTrue(ids.contains("Barney"));
- assertTrue(ids.contains("Wilma"));
-
- ServiceDef sd = md.getServiceDef("Fred");
-
- assertEquals(sd.getServiceId(), "Fred");
-
- assertEquals(sd.getServiceInterface(), FieService.class);
-
- assertTrue(sd.toString().contains(className + ".buildFred()"));
- assertEquals(sd.getServiceScope(), ScopeConstants.DEFAULT);
- assertEquals(sd.isEagerLoad(), false);
- assertTrue(sd.getMarkers().isEmpty());
-
- sd = md.getServiceDef("Wilma");
- assertEquals(sd.isEagerLoad(), true);
-
- // Now the decorator method.
-
- Set<DecoratorDef> defs = md.getDecoratorDefs();
-
- assertEquals(defs.size(), 1);
-
- DecoratorDef dd = defs.iterator().next();
-
- assertEquals(dd.getDecoratorId(), "Logging");
- assertTrue(dd.toString().contains(className + ".decorateLogging(Class, Object)"));
-
- verify();
- }
-
- @Test
- public void default_service_id_from_method_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(ServiceIdViaAnnotationModule.class, logger, null);
-
- assertEquals(def.getServiceIds().size(), 2);
-
- ServiceDef sd = def.getServiceDef("FooService");
-
- assertEquals(sd.getServiceId(), "FooService");
-
- verify();
- }
-
- @Test
- public void default_service_id_from_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(ServiceIdViaAnnotationModule.class, logger, null);
-
- assertEquals(def.getServiceIds().size(), 2);
-
- ServiceDef sd = def.getServiceDef("BarneyService");
-
- assertEquals(sd.getServiceId(), "BarneyService");
-
- verify();
- }
-
- @Test
- public void default_service_id_from_method_named_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(NamedServiceModule.class, logger, null);
-
- assertEquals(def.getServiceIds().size(), 2);
-
- ServiceDef sd = def.getServiceDef("BazService");
-
- assertEquals(sd.getServiceId(), "BazService");
-
- verify();
- }
-
- @Test
- public void default_service_id_from_named_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(NamedServiceModule.class, logger, null);
-
- assertEquals(def.getServiceIds().size(), 2);
-
- ServiceDef sd = def.getServiceDef("QuuxService");
-
- assertEquals(sd.getServiceId(), "QuuxService");
-
- verify();
- }
-
- @Test
- public void default_service_id_from_return_type()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(DefaultServiceIdModule.class, logger, null);
-
- assertEquals(def.getServiceIds().size(), 1);
-
- ServiceDef sd = def.getServiceDef("FieService");
-
- assertEquals(sd.getServiceId(), "FieService");
-
- verify();
- }
-
- /**
- * Two different methods both claim to build the same service.
- */
- @Test
- public void service_id_conflict() throws Exception
- {
- Method conflictMethod = ServiceIdConflictMethodModule.class.getMethod("buildFred");
- String conflictMethodString = InternalUtils.asString(conflictMethod, proxyFactory);
-
- String expectedMethod = InternalUtils.asString(
- ServiceIdConflictMethodModule.class.getMethod("buildFred", Object.class), proxyFactory);
-
- Logger logger = mockLogger();
-
- replay();
-
- // BigDecimal is arbitrary, any class would do.
-
- try
- {
- new DefaultModuleDefImpl(ServiceIdConflictMethodModule.class, logger, proxyFactory);
-
- unreachable();
- } catch (RuntimeException ex)
- {
- assertMessageContains(
- ex,
- "Service Fred (defined by org.apache.tapestry5.ioc.internal.ServiceIdConflictMethodModule.buildFred()",
- "conflicts with previously defined service defined by org.apache.tapestry5.ioc.internal.ServiceIdConflictMethodModule.buildFred(Object)");
- }
-
- verify();
- }
-
- @Test
- public void builder_method_returns_void() throws Exception
- {
- Method m = VoidBuilderMethodModule.class.getMethod("buildNull");
-
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(VoidBuilderMethodModule.class, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertEquals(ex.getMessage(), IOCMessages.buildMethodWrongReturnType(m));
- }
-
- verify();
- }
-
- @Test
- public void builder_method_returns_array() throws Exception
- {
- Method m = BuilderMethodModule.class.getMethod("buildStringArray");
-
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(BuilderMethodModule.class, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertEquals(ex.getMessage(), IOCMessages.buildMethodWrongReturnType(m));
- }
-
- verify();
- }
-
- @Test
- public void decorator_method_returns_void() throws Exception
- {
- invalidDecoratorMethod(VoidDecoratorMethodModule.class, "decorateVoid");
- }
-
- private void invalidDecoratorMethod(Class moduleClass, String methodName) throws NoSuchMethodException
- {
- Method m = moduleClass.getMethod(methodName, Object.class);
-
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(moduleClass, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertEquals(ex.getMessage(), IOCMessages.decoratorMethodWrongReturnType(m));
- }
-
- verify();
- }
-
- @Test
- public void decorator_method_returns_primitive() throws Exception
- {
- invalidDecoratorMethod(PrimitiveDecoratorMethodModule.class, "decoratePrimitive");
- }
-
- @Test
- public void decorator_method_returns_array() throws Exception
- {
- invalidDecoratorMethod(ArrayDecoratorMethodModule.class, "decorateArray");
- }
-
- @Test
- public void contribution_without_annotation()
- {
- attemptConfigurationMethod(SimpleModule.class, "Barney", "contributeBarney(Configuration)");
- }
-
- @Test
- public void ordered_contribution_method()
- {
- attemptConfigurationMethod(OrderedConfigurationModule.class, "Ordered",
- "contributeOrdered(OrderedConfiguration)");
- }
-
- @Test
- public void mapped_contribution_method()
- {
- attemptConfigurationMethod(MappedConfigurationModule.class, "Mapped", "contributeMapped(MappedConfiguration)");
- }
-
- private void attemptConfigurationMethod(Class moduleClass, String expectedServiceId, String expectedMethodSignature)
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(moduleClass, logger, proxyFactory);
-
- Set<ContributionDef> defs = md.getContributionDefs();
-
- assertEquals(defs.size(), 1);
-
- ContributionDef cd = defs.iterator().next();
-
- // The target service id is derived from the method name
-
- assertEquals(cd.getServiceId(), expectedServiceId);
-
- // Can't be exact, because the source file & line number are probably attached (and those
- // can change)
-
- assertTrue(cd.toString().contains(moduleClass.getName() + "." + expectedMethodSignature));
-
- verify();
- }
-
- @Test
- public void contribution_with_too_many_parameters() throws Exception
- {
- Class moduleClass = TooManyContributionParametersModule.class;
- Method m = findMethod(moduleClass, "contributeTooMany");
-
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(moduleClass, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertEquals(
- ex.getMessage(),
- "Service contribution method org.apache.tapestry5.ioc.internal.TooManyContributionParametersModule.contributeTooMany(Configuration, OrderedConfiguration) contains more than one parameter of type Configuration, OrderedConfiguration, or MappedConfiguration. Exactly one such parameter is required for a service contribution method.");
- }
-
- verify();
- }
-
- @Test
- public void contribution_with_no_contribution_parameter() throws Exception
- {
- Class moduleClass = NoUsableContributionParameterModule.class;
- Method m = findMethod(moduleClass, "contributeNoParameter");
-
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(moduleClass, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertEquals(
- ex.getMessage(),
- "Service contribution method org.apache.tapestry5.ioc.internal.NoUsableContributionParameterModule.contributeNoParameter(UpcaseService) does not contain a parameter of type Configuration, OrderedConfiguration or MappedConfiguration. This parameter is how the method make contributions into the service's configuration.");
- }
-
- verify();
- }
-
- @Test
- public void simple_binder_method()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(AutobuildModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("StringHolder");
-
- assertEquals(sd.getServiceInterface(), StringHolder.class);
- assertEquals(sd.getServiceId(), "StringHolder");
- assertEquals(sd.getServiceScope(), ScopeConstants.DEFAULT);
- assertFalse(sd.isEagerLoad());
-
- verify();
- }
-
- @Test
- public void bind_service_with_all_options()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(ComplexAutobuildModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("SH");
-
- assertEquals(sd.getServiceInterface(), StringHolder.class);
- assertEquals(sd.getServiceId(), "SH");
- assertEquals(sd.getServiceScope(), "magic");
- assertTrue(sd.isEagerLoad());
-
- verify();
- }
-
- @Test
- public void attempt_to_bind_a_service_with_no_public_constructor()
- {
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(UninstantiableAutobuildServiceModule.class, logger, null);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertMessageContains(
- ex,
- "Class org.apache.tapestry5.ioc.internal.RunnableServiceImpl (implementation of service \'Runnable\') does not contain any public constructors.");
- }
-
- verify();
- }
-
- @Test
- public void instance_method_bind_is_error()
- {
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(NonStaticBindMethodModule.class, logger, proxyFactory);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertMessageContains(ex,
- "Method org.apache.tapestry5.ioc.internal.NonStaticBindMethodModule.bind(ServiceBinder)",
- "appears to be a service binder method, but is an instance method, not a static method.");
- }
-
- verify();
- }
-
- @Test
- public void multiple_constructors_on_autobuild_service_implementation()
- {
- Logger logger = mockLogger();
- ServiceBuilderResources resources = mockServiceBuilderResources();
-
- train_getTracker(resources, tracker);
-
- // The point is, we're choosing the constructor with the largest number of parameters.
-
- logger.debug(contains("org.apache.tapestry5.ioc.internal.MultipleConstructorsAutobuildService(StringHolder)"));
-
- train_getServiceId(resources, "StringHolder");
- train_getLogger(resources, logger);
- train_getServiceInterface(resources, StringHolder.class);
- train_getService(resources, "ToUpperCaseStringHolder", StringHolder.class, new ToUpperCaseStringHolder());
-
- replay();
-
- ModuleDef def = new DefaultModuleDefImpl(MutlipleAutobuildServiceConstructorsModule.class, logger, proxyFactory);
-
- ServiceDef sd = def.getServiceDef("StringHolder");
-
- assertNotNull(sd);
-
- ObjectCreator oc = sd.createServiceCreator(resources);
-
- StringHolder holder = (StringHolder) oc.createObject();
-
- holder.setValue("foo");
- assertEquals(holder.getValue(), "FOO");
-
- verify();
- }
-
- @Test
- public void exception_from_inside_bind_method()
- {
- Logger logger = mockLogger();
-
- replay();
-
- try
- {
- new DefaultModuleDefImpl(ExceptionInBindMethod.class, logger, proxyFactory);
- unreachable();
- } catch (RuntimeException ex)
- {
- assertTrue(ex.getMessage().matches(
- "Error invoking service binder method org.apache.tapestry5.ioc.internal.ExceptionInBindMethod.bind\\(ServiceBinder\\) "
- + "\\(at ExceptionInBindMethod.java:\\d+\\): Really, how often is this going to happen\\?"));
- }
-
- verify();
- }
-
- @Test
- public void autoload_service_is_eager_load_via_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(EagerLoadViaAnnotationModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("Runnable");
-
- assertTrue(sd.isEagerLoad());
-
- verify();
- }
-
- @Test
- public void service_builder_method_has_marker_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(MarkerModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("Greeter");
-
- assertListsEquals(CollectionFactory.newList(sd.getMarkers()), BlueMarker.class);
-
- verify();
- }
-
- @Test
- public void bound_service_has_marker_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(MarkerModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("RedGreeter");
-
- assertListsEquals(CollectionFactory.newList(sd.getMarkers()), RedMarker.class);
-
- verify();
- }
-
- @Test
- public void bound_service_explicit_marker()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(MarkerModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("SecondRedGreeter");
-
- assertListsEquals(CollectionFactory.newList(sd.getMarkers()), RedMarker.class);
-
- verify();
- }
-
- @SuppressWarnings("unchecked")
- @Test
- public void explicit_marker_overrides_marker_annotation()
- {
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(MarkerModule.class, logger, null);
-
- ServiceDef sd = md.getServiceDef("SurprisinglyBlueGreeter");
-
- // BlueMarker from ServiceBindingOptions, RedMarker from @Marker on class
-
- Set<Class> markers = sd.getMarkers();
-
- assertTrue(markers.contains(RedMarker.class));
- assertTrue(markers.contains(BlueMarker.class));
- assertEquals(markers.size(), 2);
-
- verify();
- }
-
- /**
- * TAP5-839
- */
- @Test
- public void public_synthetic_methods_are_ignored() throws NoSuchMethodException
- {
- Class moduleClass = createSyntheticMethodModuleClass();
-
- Logger logger = mockLogger();
-
- replay();
-
- ModuleDef md = new DefaultModuleDefImpl(moduleClass, logger, null);
-
- // reality check that a service was found
-
- assertEquals(md.getServiceIds().size(), 1);
-
- verify();
- }
-
- private Class createSyntheticMethodModuleClass() throws NoSuchMethodException
- {
-
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
-
- cw.visit(V1_5, ACC_PUBLIC, "EnhancedSyntheticMethodModule", null,
- PlasticInternalUtils.toInternalName(SyntheticMethodModule.class.getName()), null);
-
- MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC, "synth", "()V", null, null);
- mv.visitCode();
- mv.visitInsn(RETURN);
- mv.visitEnd();
-
- cw.visitEnd();
-
- byte[] bytecode = cw.toByteArray();
-
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
-
- PlasticClassLoader plasticLoader = new PlasticClassLoader(loader, new NoopClassLoaderDelegate());
-
- return plasticLoader.defineClassWithBytecode("EnhancedSyntheticMethodModule", bytecode);
- }
-
- // TODO: We're short on tests that ensure that marker annotation are additive (i.e., module
- // marker annotation are
- // merged into the set specific to the service).
-
-}