You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/06/21 19:28:19 UTC

[3/9] git commit: Refactor all the tapestry-ioc Spock specifications into the ioc.specs package Convert some package-private classes and constructors to public

Refactor all the tapestry-ioc Spock specifications into the ioc.specs package
Convert some package-private classes and constructors to public


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/a1bef869
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/a1bef869
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/a1bef869

Branch: refs/heads/master
Commit: a1bef8696b1737a15986de2ce502842de6989f00
Parents: 313b7bf
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Thu Jun 21 10:01:11 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Thu Jun 21 10:08:39 2012 -0700

----------------------------------------------------------------------
 .../ioc/internal/services/BridgeBuilder.java       |   24 +-
 .../internal/services/FilterMethodAnalyzer.java    |    2 +-
 .../ioc/internal/services/MethodIterator.java      |   14 +-
 .../ioc/internal/services/MethodSignature.java     |    8 +-
 .../ioc/internal/util/InheritanceSearch.java       |    7 +-
 .../ioc/specs/AbstractRegistrySpecification.groovy |   33 +
 .../AbstractSharedRegistrySpecification.groovy     |   46 +
 .../src/test/groovy/ioc/specs/AdvisorsSpec.groovy  |   83 ++
 .../specs/AspectInterceptorBuilderImplSpec.groovy  |  151 +++
 .../src/test/groovy/ioc/specs/AutobuildSpec.groovy |  172 ++++
 .../test/groovy/ioc/specs/BaseLocatableSpec.groovy |   36 +
 .../test/groovy/ioc/specs/BridgeBuilderSpec.groovy |  169 ++++
 .../groovy/ioc/specs/CaseInsensitiveMapSpec.groovy |  303 ++++++
 .../groovy/ioc/specs/ChainBuilderImplSpec.groovy   |  121 +++
 .../ioc/specs/ClassNameLocatorImplSpec.groovy      |   76 ++
 .../ClasspathResourceSymbolProviderSpec.groovy     |   31 +
 .../groovy/ioc/specs/ConcurrentBarrierSpec.groovy  |  146 +++
 .../groovy/ioc/specs/ConfigurationsSpec.groovy     |  317 +++++++
 .../ioc/specs/ContributionDefImplSpec.groovy       |  209 +++++
 .../groovy/ioc/specs/CronExpressionSpec.groovy     |  104 +++
 .../test/groovy/ioc/specs/CronScheduleSpec.groovy  |   26 +
 .../test/groovy/ioc/specs/DecoratorsSpec.groovy    |  110 +++
 .../DefaultImplementationBuilderImplSpec.groovy    |   39 +
 .../ioc/specs/DefaultModuleDefImplSpec.groovy      |  450 +++++++++
 .../src/test/groovy/ioc/specs/DummyLockSpec.groovy |   28 +
 .../src/test/groovy/ioc/specs/EagerLoadSpec.groovy |   22 +
 .../ioc/specs/ExceptionAnalyzerImplSpec.groovy     |  214 +++++
 .../ioc/specs/ExceptionTrackerImplSpec.groovy      |   32 +
 .../groovy/ioc/specs/ExceptionUtilsSpec.groovy     |   53 ++
 .../ioc/specs/FilterMethodAnalyzerSpec.groovy      |   37 +
 .../groovy/ioc/specs/GeneralIntegrationSpec.groovy |   25 +
 .../test/groovy/ioc/specs/GenericUtilsSpec.groovy  |   40 +
 .../groovy/ioc/specs/GlobPatternMatcherSpec.groovy |   63 ++
 .../test/groovy/ioc/specs/IdAllocatorSpec.groovy   |  163 ++++
 .../groovy/ioc/specs/InheritanceSearchSpec.groovy  |   68 ++
 .../src/test/groovy/ioc/specs/InjectionSpec.groovy |  125 +++
 .../test/groovy/ioc/specs/InternalUtilsSpec.groovy |  607 ++++++++++++
 .../ioc/specs/JustInTimeObjectCreatorSpec.groovy   |   53 ++
 .../groovy/ioc/specs/LazyAdvisorImplSpec.groovy    |  140 +++
 .../ioc/specs/LocalizedNamesGeneratorSpec.groovy   |   39 +
 .../test/groovy/ioc/specs/LocationImplSpec.groovy  |   89 ++
 .../ioc/specs/LoggingDecoratorImplSpec.groovy      |  173 ++++
 .../groovy/ioc/specs/LoggingSourceImplSpec.groovy  |   30 +
 .../groovy/ioc/specs/ManifestProcessingSpec.groovy |   37 +
 .../ioc/specs/MasterObjectProviderImplSpec.groovy  |  110 +++
 .../ioc/specs/MessageFormatterImplSpec.groovy      |   28 +
 .../test/groovy/ioc/specs/MessagesImplSpec.groovy  |   76 ++
 .../groovy/ioc/specs/MethodIteratorSpec.groovy     |  115 +++
 .../groovy/ioc/specs/MethodSignatureSpec.groovy    |  179 ++++
 .../test/groovy/ioc/specs/ModuleImplSpec.groovy    |  280 ++++++
 .../ioc/specs/ModuleInstantiationSpec.groovy       |   55 ++
 .../ioc/specs/NonParallelExecutorSpec.groovy       |   59 ++
 .../test/groovy/ioc/specs/OneShotLockSpec.groovy   |   42 +
 .../groovy/ioc/specs/OperationAdvisorSpec.groovy   |   87 ++
 .../ioc/specs/OrderedConstraintBuilderSpec.groovy  |   31 +
 .../src/test/groovy/ioc/specs/OrdererSpec.groovy   |  285 ++++++
 .../groovy/ioc/specs/ParallelExecutorSpec.groovy   |  104 +++
 .../groovy/ioc/specs/PerThreadScopeSpec.groovy     |   91 ++
 .../groovy/ioc/specs/PeriodicExecutorSpec.groovy   |   29 +
 .../ioc/specs/PerthreadManagerImplSpec.groovy      |  183 ++++
 .../ioc/specs/PipelineBuilderImplSpec.groovy       |   87 ++
 .../groovy/ioc/specs/PropertyAccessImplSpec.groovy |  709 +++++++++++++++
 .../ioc/specs/PropertyShadowBuilderImplSpec.groovy |  121 +++
 ...RecursiveServiceCreationCheckWrapperSpec.groovy |   82 ++
 .../groovy/ioc/specs/RegistryBuilderSpec.groovy    |   96 ++
 ...RegistryConstructionAndRuntimeErrorsSpec.groovy |  118 +++
 .../src/test/groovy/ioc/specs/RegistrySpec.groovy  |   56 ++
 .../groovy/ioc/specs/RegistryStartupSpec.groovy    |   97 ++
 .../ioc/specs/RegistryshutdownHubImplSpec.groovy   |  106 +++
 .../src/test/groovy/ioc/specs/ReloadSpec.groovy    |  403 ++++++++
 .../ioc/specs/ResourceSymbolProviderSpec.groovy    |   32 +
 .../ioc/specs/ServiceActivityScoreboardSpec.groovy |   75 ++
 .../test/groovy/ioc/specs/ServiceBinderSpec.groovy |   37 +
 .../specs/ServiceBuilderMethodInvokerSpec.groovy   |  199 ++++
 .../ioc/specs/ServiceCreatorGenericsSpec.groovy    |   82 ++
 .../test/groovy/ioc/specs/ServiceLookupSpec.groovy |  118 +++
 .../test/groovy/ioc/specs/ServiceProxySpec.groovy  |   98 ++
 .../src/test/groovy/ioc/specs/StackSpec.groovy     |  113 +++
 .../ioc/specs/StrategyBuilderImplSpec.groovy       |   69 ++
 .../groovy/ioc/specs/StrategyRegistrySpec.groovy   |  148 +++
 .../ioc/specs/StringToEnumCoercionSpec.groovy      |   53 ++
 .../ioc/specs/SystemEnvSymbolProviderSpec.groovy   |   21 +
 .../test/groovy/ioc/specs/TimeIntervalSpec.groovy  |   72 ++
 .../src/test/groovy/ioc/specs/ToString.groovy      |    7 +
 .../groovy/ioc/specs/URLChangeTrackerSpec.groovy   |  187 ++++
 .../ValidatingConfigurationWrapperSpec.groovy      |   80 ++
 ...ValidatingMappedConfigurationWrapperSpec.groovy |  154 ++++
 ...alidatingOrderedConfigurationWrapperSpec.groovy |   82 ++
 .../ioc/AbstractRegistrySpecification.groovy       |   31 -
 .../ioc/AbstractSharedRegistrySpecification.groovy |   44 -
 .../org/apache/tapestry5/ioc/AdvisorsSpec.groovy   |   83 --
 .../org/apache/tapestry5/ioc/AutobuildSpec.groovy  |  171 ----
 .../apache/tapestry5/ioc/BaseLocatableSpec.groovy  |   33 -
 .../apache/tapestry5/ioc/ConfigurationsSpec.groovy |  316 -------
 .../org/apache/tapestry5/ioc/DecoratorsSpec.groovy |  109 ---
 .../org/apache/tapestry5/ioc/EagerLoadSpec.groovy  |   20 -
 .../org/apache/tapestry5/ioc/InjectionSpec.groovy  |  123 ---
 .../tapestry5/ioc/ManifestProcessingSpec.groovy    |   36 -
 .../tapestry5/ioc/ModuleInstantiationSpec.groovy   |   53 --
 .../ioc/OrderedConstraintBuilderSpec.groovy        |   30 -
 .../apache/tapestry5/ioc/PerThreadScopeSpec.groovy |   88 --
 .../tapestry5/ioc/RegistryBuilderSpec.groovy       |   95 --
 ...RegistryConstructionAndRuntimeErrorsSpec.groovy |  117 ---
 .../org/apache/tapestry5/ioc/RegistrySpec.groovy   |   53 --
 .../org/apache/tapestry5/ioc/ReloadSpec.groovy     |  401 --------
 .../ioc/ServiceActivityScoreboardSpec.groovy       |   74 --
 .../apache/tapestry5/ioc/ServiceBinderSpec.groovy  |   35 -
 .../apache/tapestry5/ioc/ServiceLookupSpec.groovy  |  116 ---
 .../apache/tapestry5/ioc/ServiceProxySpec.groovy   |   96 --
 .../ioc/internal/ContributionDefImplSpec.groovy    |  208 -----
 .../ioc/internal/DefaultModuleDefImplSpec.groovy   |  449 ---------
 .../ioc/internal/GlobPatternMatcherSpec.groovy     |   63 --
 .../ioc/internal/LazyAdvisorImplSpec.groovy        |  141 ---
 .../ioc/internal/LoggingDecoratorImplSpec.groovy   |  172 ----
 .../ioc/internal/LoggingSourceImplSpec.groovy      |   29 -
 .../tapestry5/ioc/internal/ModuleImplSpec.groovy   |  280 ------
 ...RecursiveServiceCreationCheckWrapperSpec.groovy |   80 --
 .../ServiceBuilderMethodInvokerSpec.groovy         |  195 ----
 .../ioc/internal/ServiceCreatorGenericsSpec.groovy |   79 --
 .../ValidatingConfigurationWrapperSpec.groovy      |   78 --
 ...ValidatingMappedConfigurationWrapperSpec.groovy |  152 ---
 ...alidatingOrderedConfigurationWrapperSpec.groovy |   80 --
 .../AspectInterceptorBuilderImplSpec.groovy        |  152 ---
 .../ioc/internal/services/BridgeBuilderSpec.groovy |  169 ----
 .../internal/services/ChainBuilderImplSpec.groovy  |  122 ---
 .../services/ClassNameLocatorImplSpec.groovy       |   74 --
 .../ClasspathResourceSymbolProviderSpec.groovy     |   30 -
 .../DefaultImplementationBuilderImplSpec.groovy    |   40 -
 .../services/ExceptionAnalyzerImplSpec.groovy      |  215 -----
 .../services/ExceptionTrackerImplSpec.groovy       |   32 -
 .../services/FilterMethodAnalyzerSpec.groovy       |   33 -
 .../services/JustInTimeObjectCreatorSpec.groovy    |   52 --
 .../services/MasterObjectProviderImplSpec.groovy   |  109 ---
 .../internal/services/MethodIteratorSpec.groovy    |  113 ---
 .../internal/services/MethodSignatureSpec.groovy   |  178 ----
 .../services/NonParallelExecutorSpec.groovy        |   58 --
 .../internal/services/ParallelExecutorSpec.groovy  |  105 ---
 .../services/PerthreadManagerImplSpec.groovy       |  182 ----
 .../services/PipelineBuilderImplSpec.groovy        |   86 --
 .../services/PropertyAccessImplSpec.groovy         |  708 --------------
 .../services/PropertyShadowBuilderImplSpec.groovy  |  122 ---
 .../internal/services/RegistryStartupSpec.groovy   |   95 --
 .../services/RegistryshutdownHubImplSpec.groovy    |  105 ---
 .../services/ResourceSymbolProviderSpec.groovy     |   31 -
 .../services/StrategyBuilderImplSpec.groovy        |   70 --
 .../ioc/internal/services/ToString.groovy          |    7 -
 .../services/cron/CronExpressionSpec.groovy        |  103 ---
 .../tapestry5/ioc/services/CronScheduleSpec.groovy |   27 -
 .../ioc/services/GeneralIntegrationSpec.groovy     |   26 -
 .../ioc/services/OperationAdvisorSpec.groovy       |   83 --
 .../ioc/services/PeriodicExecutorSpec.groovy       |   30 -
 .../services/SystemEnvSymbolProviderSpec.groovy    |   20 -
 .../ioc/util/CaseInsensitiveMapSpec.groovy         |  303 ------
 .../ioc/util/ConcurrentBarrierSpec.groovy          |  146 ---
 .../apache/tapestry5/ioc/util/DummyLockSpec.groovy |   28 -
 .../tapestry5/ioc/util/GenericUtilsSpec.groovy     |   40 -
 .../ioc/util/InheritanceSearchSpec.groovy          |   68 --
 .../tapestry5/ioc/util/InternalUtilsSpec.groovy    |  607 ------------
 .../tapestry5/ioc/util/LocationImplSpec.groovy     |   89 --
 .../ioc/util/MessageFormatterImplSpec.groovy       |   28 -
 .../tapestry5/ioc/util/MessagesImplSpec.groovy     |   76 --
 .../tapestry5/ioc/util/OneShotLockSpec.groovy      |   42 -
 .../apache/tapestry5/ioc/util/OrdererSpec.groovy   |  285 ------
 .../tapestry5/ioc/util/URLChangeTrackerSpec.groovy |  187 ----
 .../tapestry5/util/ExceptionUtilsSpec.groovy       |   53 --
 .../org/apache/tapestry5/util/StackSpec.groovy     |  113 ---
 .../tapestry5/util/StrategyRegistrySpec.groovy     |  148 ---
 .../tapestry5/util/StringToEnumCoercionSpec.groovy |   52 --
 .../apache/tapestry5/util/TimeIntervalSpec.groovy  |   72 --
 .../org/apache/tapestry5/ioc/MasterModule.java     |    4 +-
 .../test/java/org/apache/tapestry5/ioc/Square.java |    2 +-
 .../internal/ExtraPublicConstructorsModule.java    |    2 +-
 .../ioc/internal/ModuleImplTestModule.java         |    4 +-
 .../ioc/internal/PrivateConstructorModule.java     |    7 +-
 .../ioc/internal/ServiceBuilderMethodFixture.java  |   13 +-
 .../apache/tapestry5/ioc/internal/util/Bar.java    |    2 +-
 .../tapestry5/ioc/internal/util/BarImpl.java       |    2 +-
 .../apache/tapestry5/ioc/internal/util/Foo.java    |    2 +-
 .../apache/tapestry5/ioc/internal/util/FooBar.java |    2 +-
 .../tapestry5/ioc/internal/util/FooBarImpl.java    |    2 +-
 .../tapestry5/ioc/internal/util/FooImpl.java       |    2 +-
 .../ioc/internal/util/TargetMessages.java          |    2 +-
 .../tapestry5/ioc/util/IdAllocatorSpec.groovy      |  163 ----
 .../ioc/util/LocalizedNamesGeneratorSpec.groovy    |   39 -
 184 files changed, 9966 insertions(+), 9897 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/BridgeBuilder.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/BridgeBuilder.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/BridgeBuilder.java
index 4a0276c..8ff4762 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/BridgeBuilder.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/BridgeBuilder.java
@@ -14,26 +14,20 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import java.util.Iterator;
-import java.util.List;
-
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
-import org.apache.tapestry5.plastic.ClassInstantiator;
-import org.apache.tapestry5.plastic.InstructionBuilder;
-import org.apache.tapestry5.plastic.InstructionBuilderCallback;
-import org.apache.tapestry5.plastic.PlasticClass;
-import org.apache.tapestry5.plastic.PlasticClassTransformer;
-import org.apache.tapestry5.plastic.PlasticField;
-import org.apache.tapestry5.plastic.PlasticMethod;
+import org.apache.tapestry5.plastic.*;
 import org.slf4j.Logger;
 
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Used by the {@link org.apache.tapestry5.ioc.internal.services.PipelineBuilderImpl} to create bridge classes and to
  * create instances of bridge classes. A bridge class implements the <em>service</em> interface. Within the chain,
  * bridge 1 is passed to filter 1. Invoking methods on bridge 1 will invoke methods on filter 2.
  */
-class BridgeBuilder<S, F>
+public class BridgeBuilder<S, F>
 {
     private final Logger logger;
 
@@ -47,7 +41,7 @@ class BridgeBuilder<S, F>
 
     private ClassInstantiator<S> instantiator;
 
-    BridgeBuilder(Logger logger, Class<S> serviceInterface, Class<F> filterInterface, PlasticProxyFactory proxyFactory)
+    public BridgeBuilder(Logger logger, Class<S> serviceInterface, Class<F> filterInterface, PlasticProxyFactory proxyFactory)
     {
         this.logger = logger;
         this.serviceInterface = serviceInterface;
@@ -61,8 +55,10 @@ class BridgeBuilder<S, F>
     /**
      * Instantiates a bridge object.
      *
-     * @param nextBridge the next Bridge object in the pipeline, or the terminator service
-     * @param filter     the filter object for this step of the pipeline
+     * @param nextBridge
+     *         the next Bridge object in the pipeline, or the terminator service
+     * @param filter
+     *         the filter object for this step of the pipeline
      */
     public S instantiateBridge(S nextBridge, F filter)
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/FilterMethodAnalyzer.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/FilterMethodAnalyzer.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/FilterMethodAnalyzer.java
index b11d80b..9632bb0 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/FilterMethodAnalyzer.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/FilterMethodAnalyzer.java
@@ -18,7 +18,7 @@ package org.apache.tapestry5.ioc.internal.services;
  * Used by {@link org.apache.tapestry5.ioc.internal.services.PipelineBuilderImpl} to analyze service interface methods
  * against filter interface methods to find the position of the extra service parameter (in the filter method).
  */
-class FilterMethodAnalyzer
+public class FilterMethodAnalyzer
 {
     private final Class serviceInterface;
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodIterator.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodIterator.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodIterator.java
index 7cadee2..5dc4b3a 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodIterator.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodIterator.java
@@ -14,22 +14,20 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import org.apache.tapestry5.ioc.internal.services.MethodSignature;
+import java.lang.reflect.Method;
+import java.util.*;
 
 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
 
-import java.lang.reflect.Method;
-import java.util.*;
-
 /**
  * Utility used to iterate over the publically visible methods of a class or interface. The MethodIterator understands
  * some complications that can occur when a class inherits the same method from multiple interfaces and with slightly
  * different signatures (due to the fact that declared thrown exceptions can vary slightly for the "same" method).
- * 
+ *
  * @see MethodSignature#isOverridingSignatureOf(MethodSignature)
  */
-class MethodIterator
+public class MethodIterator
 {
     private boolean toString;
 
@@ -100,9 +98,9 @@ class MethodIterator
      * Returns the next method (as a {@link MethodSignature}, returning null when all are exhausted. Each method
      * signature is returned exactly once (even if the same method signature is defined in multiple inherited classes or
      * interfaces). The method signatures returned in ascending order, according to the "natural ordering".
-     * 
+     *
      * @throws NoSuchElementException
-     *             if there are no more signatures
+     *         if there are no more signatures
      */
     public MethodSignature next()
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodSignature.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodSignature.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodSignature.java
index 63ea2c2..e18a0a1 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodSignature.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/MethodSignature.java
@@ -14,12 +14,12 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import java.lang.reflect.Method;
-import java.util.Arrays;
-
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.plastic.PlasticUtils;
 
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
 /**
  * A representation of a {@link java.lang.reflect.Method}, identifying the name, return type, parameter types and
  * exception types. Actual Method objects are tied to a particular class, and don't compare well with other otherwise
@@ -30,7 +30,7 @@ import org.apache.tapestry5.plastic.PlasticUtils;
  * instance and static methods</em>.
  */
 @SuppressWarnings("all")
-class MethodSignature
+public class MethodSignature
 {
     private int hashCode = -1;
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InheritanceSearch.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InheritanceSearch.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InheritanceSearch.java
index 824f7ef..c645807 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InheritanceSearch.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InheritanceSearch.java
@@ -32,8 +32,8 @@ import java.util.Set;
  * Once all interfaces are exhausted, java.lang.Object is returned (it is always returned last).
  * <p/>
  * Two minor tweak to normal inheritance rules: <ul> <li> Normally, the parent class of an <em>object</em> array is
- * java.lang.Object, which is odd because Foo[] is assignable to Object[]. Thus, we tweak the search so that the
- * effective super class of Foo[] is Object[]. <li> The "super class" of a primtive type is its <em>wrapper type</em>,
+ * java.lang.Object, which is odd because FooService[] is assignable to Object[]. Thus, we tweak the search so that the
+ * effective super class of FooService[] is Object[]. <li> The "super class" of a primtive type is its <em>wrapper type</em>,
  * with the exception of void, whose "super class" is left at its normal value (Object.class) </ul>
  * <p/>
  * This class implements the {@link Iterable} interface, so it can be used directly in a for loop: <code> for (Class
@@ -144,7 +144,8 @@ public class InheritanceSearch implements Iterator<Class>, Iterable<Class>
     }
 
     /**
-     * @throws UnsupportedOperationException always
+     * @throws UnsupportedOperationException
+     *         always
      */
     public void remove()
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/AbstractRegistrySpecification.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AbstractRegistrySpecification.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AbstractRegistrySpecification.groovy
new file mode 100644
index 0000000..5283641
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AbstractRegistrySpecification.groovy
@@ -0,0 +1,33 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.Registry
+import org.apache.tapestry5.ioc.RegistryBuilder
+import spock.lang.AutoCleanup
+import spock.lang.Specification
+
+/**
+ * Base class for Spock specifications that use a new {@link Registry} for each feature method.
+ */
+abstract class AbstractRegistrySpecification extends Specification {
+
+  @AutoCleanup("shutdown")
+  protected Registry registry;
+
+  /**
+   * Constructs a new {@link Registry} using the indicated module classes.
+   * The Registry will be shutdown after each feature method.
+   *
+   * @param moduleClasses classes to include when building the Registry
+   */
+  protected final void buildRegistry(Class... moduleClasses) {
+
+    registry = new RegistryBuilder().add(moduleClasses).build()
+  }
+
+  /** Any unrecognized methods are evaluated against the registry. */
+  def methodMissing(String name, args) {
+    registry."$name"(* args)
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/AbstractSharedRegistrySpecification.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AbstractSharedRegistrySpecification.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AbstractSharedRegistrySpecification.groovy
new file mode 100644
index 0000000..cd4a006
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AbstractSharedRegistrySpecification.groovy
@@ -0,0 +1,46 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.IOCUtilities
+import org.apache.tapestry5.ioc.Registry
+import spock.lang.Specification
+
+import java.lang.reflect.Method
+
+/**
+ * Uses a static, shared instance of the {@link org.apache.tapestry5.ioc.Registry}.
+ * All specifications that extend from this class will share
+ * a single instance of the Registry; The Registry is created by whatever specification is created first.
+ * Missing method invocations are forwarded to the registry instance. */
+abstract class AbstractSharedRegistrySpecification extends Specification {
+
+  static Registry registry
+
+  /** Any unrecognized methods are evaluated against the shared Registry instance. */
+  def methodMissing(String name, args) {
+    registry."$name"(* args)
+  }
+
+  /** Creates the Registry if it does not already exist. */
+  def setupSpec() {
+    if (registry == null) {
+      registry = IOCUtilities.buildDefaultRegistry()
+    }
+  }
+
+  /** Invokes {@link Registry#cleanupThread()}. */
+  def cleanupSpec() {
+    registry.cleanupThread();
+  }
+
+  // TODO: the Registry is never shutdown, since there's no notification
+  // that all tests are completing.
+
+  protected Method findMethod(Object subject, String methodName) {
+    def method = subject.class.methods.find { it.name == methodName }
+
+    assert method != null
+
+    return method
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/AdvisorsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AdvisorsSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AdvisorsSpec.groovy
new file mode 100644
index 0000000..3254e5a
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AdvisorsSpec.groovy
@@ -0,0 +1,83 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.AdviseByMarkerModule
+import org.apache.tapestry5.ioc.internal.AdviseByMarkerModule2
+import org.apache.tapestry5.ioc.*
+
+class AdvisorsSpec extends AbstractRegistrySpecification {
+
+  def "advisor methods must return void"() {
+    when:
+
+    buildRegistry NonVoidAdvisorMethodModule
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Advise method org.apache.tapestry5.ioc.NonVoidAdvisorMethodModule.adviseFoo(MethodAdviceReceiver)"
+    e.message.contains "does not return void"
+  }
+
+  def "advisor methods must take a MethodAdviceReceiver parameter"() {
+    when:
+
+    buildRegistry AdviceMethodMissingAdvisorParameterModule
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Advise method org.apache.tapestry5.ioc.AdviceMethodMissingAdvisorParameterModule.adviseBar()"
+    e.message.contains "must take a parameter of type org.apache.tapestry5.ioc.MethodAdviceReceiver."
+  }
+
+  def "adding advice to services"() {
+    buildRegistry AdviceDemoModule
+
+    when:
+
+    def g = getService Greeter
+
+    then:
+
+    g.greeting == "ADVICE IS EASY!"
+  }
+
+  def "methods marked with @Advise are advisor methods"() {
+
+    buildRegistry GreeterModule2, AdviseByMarkerModule
+
+    when:
+
+    def green = getService "GreenGreeter", Greeter
+
+    then:
+
+    green.greeting == "gamma[beta[alpha[Green]]]"
+  }
+
+  def "@Advise with @Local only advises services in the same module"() {
+    buildRegistry GreeterModule2, AdviseByMarkerModule
+
+    when:
+
+    def red = getService "RedGreeter", Greeter
+
+    then:
+
+    red.greeting == "delta[Red]"
+  }
+
+  def "@Advise with id attribute"() {
+    buildRegistry AdviseByMarkerModule2
+
+    when:
+
+    def red = getService "RedGreeter", Greeter
+
+    then:
+
+    red.greeting == "beta[alpha[Red]]"
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
new file mode 100644
index 0000000..e543482
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AspectInterceptorBuilderImplSpec.groovy
@@ -0,0 +1,151 @@
+package ioc.specs
+
+import org.apache.commons.lang.StringUtils
+import org.apache.tapestry5.ioc.internal.services.TextTransformer
+import org.apache.tapestry5.ioc.services.AspectDecorator
+import org.apache.tapestry5.plastic.MethodAdvice
+import org.apache.tapestry5.plastic.MethodInvocation
+import spock.lang.Shared
+
+interface Subject {
+
+  void advised();
+
+  void notAdvised();
+}
+
+interface ArraysSubject {
+
+  String[] operation(String[] inputs);
+}
+
+class AspectInterceptorBuilderImplSpec extends AbstractSharedRegistrySpecification {
+
+  @Shared
+  private AspectDecorator decorator
+
+  def setupSpec() {
+    decorator = getService AspectDecorator
+  }
+
+  def "ensure that non-advised methods are not passed through the MethodAdvice object"() {
+    Subject delegate = Mock()
+    MethodAdvice advice = Mock()
+
+    def builder = decorator.createBuilder Subject, delegate, "<Subject>"
+
+    builder.adviseMethod Subject.getMethod("advised"), advice
+
+    Subject interceptor = builder.build()
+
+    when:
+
+    interceptor.advised()
+
+    then:
+
+    1 * advice.advise(_) >> { MethodInvocation mi ->
+      assert mi.method.name == "advised"
+      mi.proceed()
+    }
+    1 * delegate.advised()
+    0 * _
+
+    when:
+
+    interceptor.notAdvised()
+
+    then:
+
+    1 * delegate.notAdvised()
+    0 * _
+  }
+
+  def "failure when advising a method that is not in the service interface"() {
+    Subject delegate = Mock()
+    MethodAdvice advice = Mock()
+
+    def builder = decorator.createBuilder Subject, delegate, "<Subject>"
+
+    when:
+
+    builder.adviseMethod Runnable.getMethod("run"), advice
+
+    then:
+
+    IllegalArgumentException e = thrown()
+
+    e.message == "Method public abstract void java.lang.Runnable.run() is not defined for interface interface ioc.specs.Subject."
+  }
+
+  def "multiple advice for single method is processed in order"() {
+    TextTransformer delegate = Mock()
+    MethodAdvice stripFirstLetter = Mock()
+    MethodAdvice reverse = Mock()
+
+    def builder = decorator.createBuilder TextTransformer, delegate, "<TextTransformer>"
+
+    def method = TextTransformer.getMethod "transform", String
+
+    builder.adviseMethod method, stripFirstLetter
+    builder.adviseMethod method, reverse
+
+    TextTransformer advised = builder.build()
+
+    when:
+
+    def result = advised.transform "Tapestry"
+
+    then:
+
+    result == "[yrtsepa]"
+
+    1 * stripFirstLetter.advise(_) >> { MethodInvocation mi ->
+      assert mi.getParameter(0) == "Tapestry"
+      mi.setParameter 0, mi.getParameter(0).substring(1)
+      mi.proceed()
+    }
+
+    1 * reverse.advise(_) >> { MethodInvocation mi ->
+      assert mi.getParameter(0) == "apestry"
+      mi.setParameter 0, StringUtils.reverse(mi.getParameter(0))
+      mi.proceed()
+    }
+
+    1 * delegate.transform(_) >> { it }
+  }
+
+  def "arrays are allowed as method parameters and return values"() {
+    ArraysSubject delegate = Mock()
+    MethodAdvice advice = Mock()
+
+    def builder = decorator.createBuilder ArraysSubject, delegate, "unused"
+    builder.adviseAllMethods advice
+
+    ArraysSubject advised = builder.build()
+
+    when:
+
+    def result = advised.operation(["Fred", "Barney"] as String[])
+
+    then:
+
+    1 * advice.advise(_) >> { MethodInvocation it ->
+      String[] inputs = it.getParameter(0)
+
+      it.setParameter 0, inputs.collect({it.toUpperCase() }) as String[]
+
+      it.proceed()
+
+      def index = 0
+
+      it.setReturnValue it.getReturnValue().collect({ value -> "${index++}:$value" }) as String[]
+    }
+
+    1 * delegate.operation(_) >> { it[0] }
+
+    result.class == ([] as String[]).class
+    result.asType(List) == ["0:FRED", "1:BARNEY"]
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/AutobuildSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/AutobuildSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/AutobuildSpec.groovy
new file mode 100644
index 0000000..78d46ed
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/AutobuildSpec.groovy
@@ -0,0 +1,172 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.ExceptionInConstructorModule
+import org.apache.tapestry5.ioc.*
+
+class AutobuildSpec extends AbstractRegistrySpecification {
+
+  def "ensure that services defined with bind() are automatically built"() {
+    buildRegistry AutobuildModule
+
+    def sh = getService StringHolder
+
+    when:
+
+    sh.value = "Foo"
+
+    then:
+
+    sh.value == "Foo"
+  }
+
+  def "check reporting of exception in autobuilt service constructor"() {
+    buildRegistry ExceptionInConstructorModule
+
+    def pingable = getService Pingable
+
+    when:
+
+    pingable.ping()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Error invoking constructor"
+    e.message.contains "ExceptionInConstructorServiceImpl()"
+    e.message.contains "Yes, we have no tomatoes."
+  }
+
+  def "bind() finds the default Impl class"() {
+    buildRegistry ConventionModule
+
+    def holder = getService StringHolder
+
+    when:
+
+    // This proves we can invoke methods, meaning that an implementation was found.
+
+    holder.value = "Bar"
+
+    then:
+
+    holder.value == "Bar"
+  }
+
+  def "validate exception reporting for no default implementation found"() {
+    when:
+
+    buildRegistry ConventionModuleImplementationNotFound
+
+    then:
+
+    RuntimeException e = thrown()
+    e.message.contains "Could not find default implementation class org.apache.tapestry5.ioc.StringTransformerImpl."
+    e.message.contains "Please provide this class, or bind the service interface to a specific implementation class."
+  }
+
+  def "validate exception reporting for incorrect implementation (that does not implement service interface)"() {
+    when:
+
+    buildRegistry ConventionFailureModule
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "No service implements the interface ${Pingable.name}"
+  }
+
+  def "service builder method can use ServiceResources.autobuild()"() {
+    buildRegistry ServiceBuilderAutobuilderModule
+
+    def holder = getService StringHolder
+
+    when:
+
+    holder.value = "Foo"
+
+    then:
+
+    holder.value == "Foo"
+  }
+
+  def "ensure that Registry.autobuild() works"() {
+
+    buildRegistry()
+
+    when:
+
+    def holder = autobuild StringHolderImpl
+
+    // This test should go further and verify that injections into StringHolderImpl work.
+
+    then:
+
+    holder.class == StringHolderImpl
+
+    when:
+
+    holder.value = "Foo"
+
+    then:
+
+    holder.value == "Foo"
+  }
+
+  def "ensure that Registry.autobuild() works (with a description)"() {
+    buildRegistry()
+
+    when:
+
+    def holder = autobuild "Building StringHolderImpl", StringHolderImpl
+
+    // This test should go further and verify that injections into StringHolderImpl work.
+
+    then:
+
+    holder.class == StringHolderImpl
+  }
+
+  def "verify exception when autobuild service implementation is not valid"() {
+    buildRegistry ServiceBuilderAutobuilderModule
+
+    def pingable = getService Pingable
+
+    when:
+
+    pingable.ping()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Class org.apache.tapestry5.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild."
+  }
+
+  def "verify exception when autobuild class has not valid constructor"() {
+    buildRegistry()
+
+    when:
+
+    autobuild UnbuildablePingable
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Class org.apache.tapestry5.ioc.UnbuildablePingable does not contain a public constructor needed to autobuild."
+  }
+
+  def "a service build method may include a parameter with @Autobuild"() {
+    buildRegistry AutobuildInjectionModule
+
+    when:
+
+    def tx = getService StringTransformer
+
+    then:
+
+    tx.transform('Hello, ${fred}') == "Hello, flintstone"
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/BaseLocatableSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/BaseLocatableSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/BaseLocatableSpec.groovy
new file mode 100644
index 0000000..d54463c
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/BaseLocatableSpec.groovy
@@ -0,0 +1,36 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.BaseLocatable
+import org.apache.tapestry5.ioc.Locatable
+import org.apache.tapestry5.ioc.Location
+import spock.lang.Specification
+
+class LocatableFixture extends BaseLocatable {
+
+  LocatableFixture(Location location) {
+    super(location)
+  }
+}
+
+class BaseLocatableSpec extends Specification {
+
+  def "location property is readable"() {
+    Location location = Mock()
+
+    Locatable locatable = new LocatableFixture(location)
+
+    expect:
+
+    locatable.location.is location
+  }
+
+  def "location may be null"() {
+    Locatable locatable = new LocatableFixture(null);
+
+
+    expect:
+
+    locatable.location == null
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/BridgeBuilderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/BridgeBuilderSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/BridgeBuilderSpec.groovy
new file mode 100644
index 0000000..402d3a9
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/BridgeBuilderSpec.groovy
@@ -0,0 +1,169 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory
+import org.slf4j.Logger
+import spock.lang.Shared
+import org.apache.tapestry5.ioc.internal.services.*
+
+class BridgeBuilderSpec extends AbstractSharedRegistrySpecification {
+
+  @Shared
+  PlasticProxyFactory proxyFactory;
+
+  def setupSpec() {
+    proxyFactory = getService PlasticProxyFactory
+  }
+
+  def "toString() of proxy is as expected"() {
+    Logger logger = Mock()
+    StandardFilter sf = Mock()
+    StandardService ss = Mock()
+
+    BridgeBuilder builder = new BridgeBuilder(logger, StandardService, StandardFilter, proxyFactory)
+
+    when:
+
+    def bridge = builder.instantiateBridge(ss, sf)
+
+    then:
+
+    bridge.toString() == "<PipelineBridge from org.apache.tapestry5.ioc.internal.services.StandardService to org.apache.tapestry5.ioc.internal.services.StandardFilter>"
+  }
+
+  def "standard service and interface"() {
+    Logger logger = Mock()
+    StandardFilter sf = Mock()
+    StandardService ss = Mock()
+
+    BridgeBuilder builder = new BridgeBuilder(logger, StandardService, StandardFilter, proxyFactory)
+    def bridge = builder.instantiateBridge(ss, sf)
+
+    when:
+
+    assert bridge.run(5) == 18
+
+    // 18 =  3 * (5 + 1)
+    // so the filter runs first, and passes 6 to the service
+    // seems there's an issue in Spock with chaining mocks this way
+
+    then:
+
+    1 * sf.run(_, _) >> { i, service -> service.run(i + 1) }
+
+    1 * ss.run(_) >> { i -> 3 * i }
+
+    0 * _
+  }
+
+  def "when toString() is part of service interface, it is forwarded through the filter"() {
+    Logger logger = Mock()
+
+    ToStringService service = new ToStringService() {
+
+      String toString() { "Service" }
+    }
+
+    ToStringFilter filter = new ToStringFilter() {
+
+      String toString(ToStringService s) {
+        s.toString().toUpperCase()
+      }
+    }
+
+    BridgeBuilder builder = new BridgeBuilder(logger, ToStringService, ToStringFilter, proxyFactory)
+
+    when:
+
+    ToStringService bridge = builder.instantiateBridge(service, filter)
+
+    then:
+
+    bridge.toString() == "SERVICE"
+  }
+
+  def "unmatched service interface method is logged and exception thrown"() {
+    Logger logger = Mock()
+    ExtraServiceMethod next = Mock()
+    Serializable filter = Mock()
+
+    BridgeBuilder builder = new BridgeBuilder(logger, ExtraServiceMethod, Serializable, proxyFactory)
+
+    when:
+
+    ExtraServiceMethod esm = builder.instantiateBridge(next, filter)
+
+    then:
+
+    1 * logger.error("Method void extraServiceMethod() has no match in filter interface java.io.Serializable.")
+
+    when:
+
+    esm.extraServiceMethod()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == "Method void extraServiceMethod() has no match in filter interface java.io.Serializable."
+  }
+
+  def "extra methods in filter interface are logged and ignored"() {
+    Logger logger = Mock()
+    Serializable next = Mock()
+    ExtraFilterMethod filter = Mock()
+
+    BridgeBuilder builder = new BridgeBuilder(logger, Serializable, ExtraFilterMethod, proxyFactory)
+
+    when:
+
+    assert builder.instantiateBridge(next, filter) != null
+
+    then:
+
+    1 * logger.error("Method void extraFilterMethod() of filter interface org.apache.tapestry5.ioc.internal.services.ExtraFilterMethod does not have a matching method in java.io.Serializable.")
+
+    0 * _
+  }
+
+  def "the service parameter may be a middle parameter of the filter method"() {
+    Logger logger = Mock()
+
+    MiddleFilter mf = new MiddleFilter() {
+
+      @Override
+      void execute(int count, char ch, MiddleService service, StringBuilder buffer) {
+        service.execute(count, ch, buffer)
+
+        buffer.append(' ')
+
+        service.execute(count + 1, Character.toUpperCase(ch), buffer)
+      }
+    }
+
+    MiddleService ms = new MiddleService() {
+
+      @Override
+      void execute(int count, char ch, StringBuilder buffer) {
+        count.times() { buffer.append ch }
+      }
+    }
+
+    BridgeBuilder builder = new BridgeBuilder(logger, MiddleService, MiddleFilter, proxyFactory)
+
+
+    MiddleService bridge = builder.instantiateBridge(ms, mf)
+
+    StringBuilder buffer = new StringBuilder("CODE: ")
+
+    when:
+
+
+    bridge.execute(3, 'a' as char, buffer)
+
+    then:
+
+    buffer.toString() == "CODE: aaa AAAA"
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy
new file mode 100644
index 0000000..c4e43f0
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/CaseInsensitiveMapSpec.groovy
@@ -0,0 +1,303 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.util.CaseInsensitiveMap
+import spock.lang.Specification
+
+class CaseInsensitiveMapSpec extends Specification {
+
+  CaseInsensitiveMap map = new CaseInsensitiveMap([fred: "flintstone", barney: "rubble", wilma: "flinstone", betty: "rubble"])
+
+  def "get() is case insensitive"() {
+    def map = new CaseInsensitiveMap()
+
+    def value = "flintstone"
+
+    when:
+
+    map.put("fred", value)
+
+    then:
+
+    map.get("fred").is(value)
+    map.get("Fred").is(value)
+  }
+
+  def "containsKey() is case insensitive"() {
+
+    expect:
+
+    map.containsKey("fred")
+    map.containsKey("Fred")
+    map.containsKey("barney")
+    map.containsKey("wilma")
+    !map.containsKey("dino")
+  }
+
+  def "remove() is case insensitive"() {
+    expect:
+
+    map.containsKey("fred")
+    !map.isEmpty()
+
+    when:
+
+    map.remove("FrED")
+
+    then:
+
+    map.keySet() == ["barney", "wilma", "betty"] as Set
+  }
+
+  def "copying Map constructor"() {
+    def standard = [fred: "flintstone", barney: "rubble", wilma: "flintstone"]
+
+    when:
+
+    def original = new CaseInsensitiveMap(standard)
+
+    then:
+
+    original == standard
+
+    when:
+
+    def copy = new CaseInsensitiveMap(original)
+
+    then:
+
+    copy == original
+  }
+
+  def "comparison of two CaseInsensitiveMaps ignores case"() {
+    def lower = new CaseInsensitiveMap([fred: "flintstone", barney: "rubble"])
+    def upper = new CaseInsensitiveMap([Fred: "flintstone", Barney: "rubble"])
+
+    expect:
+
+    upper == lower
+  }
+
+  def "put with different case replaces the old key"() {
+
+    expect:
+
+    map.keySet() == ["fred", "barney", "betty", "wilma"] as Set
+
+
+    when:
+
+    map.put("FRED", "flintstone")
+
+    then:
+
+    map.keySet() == ["FRED", "barney", "betty", "wilma"] as Set
+  }
+
+  def "get with missing key is null"() {
+    expect:
+
+    map.notFound == null
+  }
+
+  def "get with non-string key is null"() {
+    expect:
+
+    map.get(this) == null
+  }
+
+  def "expansion of the internal entry array"() {
+
+    def count = 2000
+
+    def map = new CaseInsensitiveMap()
+
+    count.times { it ->
+      assert map.put("key_$it" as String, it) == null
+    }
+
+    when:
+
+    count.times { it ->
+      assert map.get("key_$it" as String) == it
+    }
+
+    then:
+
+    map.size() == count
+    map.entrySet().size() == count
+
+    when:
+
+    map.clear()
+
+    then:
+
+    map.size() == 0
+
+  }
+
+  def "change value via entrySet()"() {
+    def map = new CaseInsensitiveMap()
+
+    map.put("fred", "flintstone")
+
+    when:
+
+    map.entrySet().each { entry -> entry.value = "murray" }
+
+    then:
+
+    map.get("fred") == "murray"
+  }
+
+  def "entrySet iterator fails fast after remove"() {
+
+    def i = map.entrySet().iterator()
+
+    i.next()
+    map.remove("betty")
+
+    when:
+
+    i.next()
+
+    then:
+
+    thrown(ConcurrentModificationException)
+  }
+
+  def "entrySet iterator fails fast after put"() {
+
+    def i = map.entrySet().iterator()
+
+    i.next()
+    map.put("zaphod", "breeblebrox")
+
+    when:
+
+    i.next()
+
+    then:
+
+    thrown(ConcurrentModificationException)
+  }
+
+  def "iterator may remove without concurrent exception"() {
+
+    def i = map.entrySet().iterator()
+
+    while (i.hasNext()) {
+      if (i.next().key == "wilma") { i.remove() }
+    }
+
+    expect:
+
+    map.keySet() == ["barney", "betty", "fred"] as Set
+  }
+
+  def "contains via entrySet"() {
+
+    def set = map.entrySet()
+
+    expect:
+
+    set.contains(newMapEntry("fred", "flintstone"))
+    set.contains(newMapEntry("Fred", "flintstone"))
+
+    !set.contains(newMapEntry("Zaphod", "Breeblebox"))
+    !set.contains(newMapEntry("fred", "murray"))
+  }
+
+  def "remove via entrySet"() {
+
+    def set = map.entrySet()
+
+    when:
+
+    assert set.remove(newMapEntry("Zaphod", "Breeblrox")) == false
+    assert set.remove(newMapEntry("fred", "murray")) == false
+
+    assert set.remove(newMapEntry("fred", "flintstone")) == true
+
+    then:
+
+    map.keySet() == ["barney", "wilma", "betty"] as Set
+  }
+
+  def newMapEntry(key, value) {
+    return new Map.Entry() {
+
+      @Override
+      Object getKey() {
+        return key
+      }
+
+      @Override
+      Object getValue() {
+        return value;
+      }
+
+      @Override
+      Object setValue(Object newValue) {
+        value = newValue
+      }
+    }
+  }
+
+  def "null is a valid key"() {
+    when:
+
+    map.put(null, "NULL")
+
+    then:
+
+    map.get(null) == "NULL"
+  }
+
+  def "clearing the entrySet clears the map"() {
+    expect:
+
+    !map.isEmpty()
+    !map.entrySet().isEmpty()
+
+    when:
+
+    map.entrySet().clear()
+
+    then:
+
+    map.isEmpty()
+  }
+
+  def "next() after last entry in entrySet is a failure"() {
+    Iterator i = map.entrySet().iterator()
+
+    while (i.hasNext()) { i.next() }
+
+    when:
+
+    i.next()
+
+    then:
+
+    thrown(NoSuchElementException)
+  }
+
+  def "serialize/deserialize copies all data"() {
+
+    def baos = new ByteArrayOutputStream()
+    def oos = new ObjectOutputStream(baos)
+
+    oos.writeObject(map)
+    oos.close()
+
+    def bais = new ByteArrayInputStream(baos.toByteArray())
+    ObjectInputStream ois = new ObjectInputStream(bais)
+
+    def copy = ois.readObject()
+
+    expect:
+
+    copy == map
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
new file mode 100644
index 0000000..7638df0
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ChainBuilderImplSpec.groovy
@@ -0,0 +1,121 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.ChainBuilder
+
+interface ChainCommand {
+
+  void run();
+
+  int workInt(int input);
+
+  boolean workBoolean(boolean input);
+
+  double workDouble(double input);
+
+  String workString(String input);
+}
+
+class ChainBuilderImplSpec extends AbstractSharedRegistrySpecification {
+
+  ChainCommand c1 = Mock()
+  ChainCommand c2 = Mock()
+
+  ChainCommand chain = getService(ChainBuilder).build(ChainCommand, [c1, c2])
+
+  def "chaining of simple void method with no parameters"() {
+    when:
+
+    chain.run()
+
+    then:
+
+    1 * c1.run()
+
+    then:
+
+    1 * c2.run()
+    0 * _
+  }
+
+  def "chaining of method with int parameter and return type"() {
+
+    when:
+
+    assert chain.workInt(7) == 99
+
+    then:
+
+    1 * c1.workInt(7) >> 0
+
+    then:
+
+    1 * c2.workInt(7) >> 99
+    0 * _
+  }
+
+  def "verify that an int method that returns a non-zero value short-circuits the chain"() {
+    when:
+
+    assert chain.workInt(7) == 88
+
+    then:
+
+    1 * c1.workInt(7) >> 88
+    0 * _
+  }
+
+  def "verify boolean parameters, return type, and short circuiting"() {
+
+    when:
+
+    assert chain.workBoolean(true) == true
+
+    then:
+
+    1 * c1.workBoolean(true) >> false
+
+    then:
+
+    1 * c2.workBoolean(true) >> true
+    0 * _
+  }
+
+  def "verify string method parameter, return type, and short circuiting"() {
+    when:
+
+    assert chain.workString("fred") == "flintstone"
+
+    then:
+
+    1 * c1.workString("fred") >> null
+
+    then:
+
+    1 * c2.workString("fred") >> "flintstone"
+    0 * _
+  }
+
+  def "verify double method parameter, return type, and short circuiting"() {
+
+    when:
+
+    assert chain.workDouble(1.2d) == 3.14d
+
+    then:
+
+    1 * c1.workDouble(1.2d) >> 0d
+
+    then:
+
+    1 * c2.workDouble(1.2d) >> 3.14d
+    0 * _
+  }
+
+  def "chain instance has reasonable toString()"() {
+    expect:
+
+    chain.toString() == "<Command chain of ioc.specs.ChainCommand>"
+  }
+
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
new file mode 100644
index 0000000..106cb29
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ClassNameLocatorImplSpec.groovy
@@ -0,0 +1,76 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.services.ClassNameLocatorImpl
+import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl
+import org.apache.tapestry5.ioc.services.ClassNameLocator
+import spock.lang.Specification
+
+class ClassNameLocatorImplSpec extends Specification {
+
+  ClassNameLocator locator = new ClassNameLocatorImpl(new ClasspathURLConverterImpl());
+
+  def assertInList(classNames, packageName, String... expectedNames) {
+
+    expectedNames.each { name ->
+      String qualifiedName = "${packageName}.${name}"
+
+      assert classNames.contains(qualifiedName), "[$qualifiedName] not present in ${classNames.join(', ')}."
+    }
+  }
+
+  def assertNotInList(classNames, packageName, String... expectedNames) {
+
+    expectedNames.each { name ->
+      String qualifiedName = "${packageName}.${name}"
+
+      assert !classNames.contains(qualifiedName), "[$qualifiedName] should not be present in ${classNames.join(', ')}."
+    }
+  }
+
+  def "locate classes inside a JAR file on the classpath"() {
+
+    expect:
+
+    assertInList locator.locateClassNames("javax.inject"),
+        "javax.inject",
+        "Inject", "Named", "Singleton"
+  }
+
+  def "can locate classes inside a subpackage, inside a classpath JAR file"() {
+
+    expect:
+
+    assertInList locator.locateClassNames("org.slf4j"),
+        "org.slf4j",
+        "spi.MDCAdapter"
+  }
+
+  def "can locate classes in local folder, but exclude inner classes"() {
+
+    def packageName = "org.apache.tapestry5.ioc.services"
+
+    when:
+
+    def names = locator.locateClassNames packageName
+
+    then:
+
+    assertInList names, packageName, "SymbolSource", "TapestryIOCModule"
+
+    assertNotInList names, packageName, 'TapestryIOCMOdules$1'
+  }
+
+  def "can locate classes in subpackage of local folders"() {
+    def packageName = "org.apache.tapestry5"
+
+    when:
+
+    def names = locator.locateClassNames packageName
+
+    then:
+
+    assertInList names, packageName, "ioc.Orderable", "ioc.services.ChainBuilder"
+    assertNotInList names, packageName, 'services.TapestryIOCModule$1'
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSymbolProviderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSymbolProviderSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSymbolProviderSpec.groovy
new file mode 100644
index 0000000..08b9cc1
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ClasspathResourceSymbolProviderSpec.groovy
@@ -0,0 +1,31 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.services.ClasspathResourceSymbolProvider
+import spock.lang.Shared
+import spock.lang.Specification
+
+class ClasspathResourceSymbolProviderSpec extends Specification {
+
+  static final String PATH = "org/apache/tapestry5/ioc/internal/services/foo.properties"
+
+  @Shared
+  def provider = new ClasspathResourceSymbolProvider(PATH)
+
+  def "access properties"() {
+
+    expect:
+    provider.valueForSymbol("homer") == "simpson"
+    provider.valueForSymbol("monty") == "burns"
+  }
+
+  def "keys are case insensitive"() {
+    expect:
+    provider.valueForSymbol("HOMER") == "simpson"
+  }
+
+  def "non-existent keys should return null"() {
+    expect:
+    provider.valueForSymbol("marge") == null
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ConcurrentBarrierSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ConcurrentBarrierSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ConcurrentBarrierSpec.groovy
new file mode 100644
index 0000000..e1e571d
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ConcurrentBarrierSpec.groovy
@@ -0,0 +1,146 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.util.ConcurrentTarget
+import org.apache.tapestry5.ioc.internal.util.ConcurrentTargetWrapper
+import spock.lang.Specification
+
+class ConcurrentBarrierSpec extends Specification {
+
+  def target = new ConcurrentTarget()
+
+  static final int THREAD_COUNT = 1000
+
+  static final int THREAD_BLOCK_SIZE = 50
+
+  def run(op) {
+    def threads = []
+    def running = []
+
+    assert target.counter == 0
+
+    THREAD_COUNT.times {
+      def t = new Thread(op)
+
+      threads << t
+
+      if (threads.size() >= THREAD_BLOCK_SIZE) {
+        threads.each { it.start() }
+        running.addAll threads
+        threads.clear()
+      }
+    }
+
+    running.each { it.join() }
+  }
+
+  def "acquire write lock"() {
+
+    when:
+
+    run { target.incrementCounter() }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "acquire read lock while holding write lock"() {
+
+    when:
+
+    run { target.incrementCounterHard() }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "upgrade read lock to write lock"() {
+    when:
+
+    run { target.incrementIfNonNegative() }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "indirection between method with read lock and method that acquires write lock"() {
+
+    when:
+
+    run { target.incrementViaRunnable() }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "barriers are independent when multiple are involved"() {
+
+    when:
+
+    run(new ConcurrentTargetWrapper(target))
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "use tryWithWrite() to get write lock if it is available"() {
+
+    when: run {
+      def good = false
+      while (!good) { good = target.tryIncrementCounter() }
+    }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "acquire read lock when inside a tryWithWrite block"() {
+
+    when:
+
+    run {
+      def good = false
+      while (!good) { good = target.tryIncrementCounterHard() }
+    }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "read lock upgrades via tryWriteLock()"() {
+
+    when:
+
+    run {
+      def good = false
+      while (!good) { good = target.tryIncrementIfNonNegative() }
+    }
+
+    then:
+
+    target.counter == THREAD_COUNT
+  }
+
+  def "write lock timeout inside read lock"() {
+    when:
+
+    target.withRead {
+      try {
+        run {
+          assert target.tryIncrementIfNonNegative() == false
+        }
+      }
+      catch (InterruptedException e) { }
+    }
+
+    then:
+
+    target.counter == 0
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ConfigurationsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ConfigurationsSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ConfigurationsSpec.groovy
new file mode 100644
index 0000000..0594606
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ConfigurationsSpec.groovy
@@ -0,0 +1,317 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.AlphabetModule
+import org.apache.tapestry5.ioc.internal.AlphabetModule2
+import org.apache.tapestry5.ioc.services.SymbolSource
+import org.apache.tapestry5.ioc.util.NonmatchingMappedConfigurationOverrideModule
+import org.apache.tapestry5.ioc.*
+
+/** Integration tests for various types of service configurations. */
+class ConfigurationsSpec extends AbstractRegistrySpecification {
+
+  def "all contributions to unordered configuration are collected"() {
+
+    buildRegistry FredModule, BarneyModule
+
+    when:
+
+    def holder = getService "UnorderedNames", NameListHolder
+
+    then:
+
+    // We don't know the actual contribution order, the impl sorts them. Just
+    // check they are all present.
+
+    holder.names == ["Beta", "Gamma", "UnorderedNames"]
+  }
+
+  def "all contributions to order configuration are collected"() {
+    buildRegistry FredModule, BarneyModule
+
+    when:
+
+    def holder = getService "OrderedNames", NameListHolder
+
+    then:
+
+    holder.names == ["BARNEY", "FRED"]
+  }
+
+  def "all contribution to mapped configuration are collected"() {
+
+    buildRegistry FredModule, BarneyModule
+
+    def sizer = getService "Sizer", Sizer
+
+    expect:
+
+    // The contributions map different Classes to strategies; this demonstrates
+    // that all contributions have been mapped and provided to Sizer service impl.
+
+    sizer.size(null) == 0
+
+    sizer.size([1, 2, 3]) == 3
+
+    sizer.size([fred: "flintstone", barney: "rubble"]) == 2
+
+    sizer.size(this) == 1
+  }
+
+  def "can contribute a class to an unordered configuration"() {
+    buildRegistry ContributeByClassModule
+
+    when:
+
+    def tx = getService "MasterStringTransformer", StringTransformer
+
+    then:
+
+    tx.transform("Tapestry") == "TAPESTRY"
+  }
+
+  def "can contribute a class to an ordered configuration"() {
+    buildRegistry ContributeByClassModule
+
+    when:
+
+    def tx = getService "StringTransformerChain", StringTransformer
+
+    then:
+
+    tx.transform("Tapestry") == "TAPESTRY"
+  }
+
+  def "can contribute class to a mapped configuration"() {
+    buildRegistry ContributeByClassModule
+
+    when:
+
+    def tx = getService "MappedStringTransformer", StringTransformer
+
+    then:
+
+    tx.transform("Tapestry") == "TAPESTRY"
+  }
+
+  def "contribution to an unknown configuration is detected as an exception"() {
+    when:
+    buildRegistry InvalidContributeDefModule
+    then:
+    IllegalArgumentException e = thrown()
+
+    e.message.contains "Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule.contributeDoesNotExist(Configuration)"
+    e.message.contains "is for service 'DoesNotExist', which does not exist."
+  }
+
+  def "a value in an ordered configuration may be overridden"() {
+    buildRegistry FredModule, BarneyModule, ConfigurationOverrideModule
+
+    when:
+
+    def holder = getService "OrderedNames", NameListHolder
+
+    then:
+
+    holder.names == ["BARNEY", "WILMA", "Mr. Flintstone"]
+  }
+
+  def "an override value in an ordered configuration must match a normally contributed value"() {
+    buildRegistry FredModule, BarneyModule, FailedConfigurationOverrideModule
+
+    def holder = getService "OrderedNames", NameListHolder
+
+    when:
+
+
+    holder.names
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Failure processing override from org.apache.tapestry5.ioc.FailedConfigurationOverrideModule.contributeOrderedNames(OrderedConfiguration)"
+    e.message.contains "Override for object 'wilma' is invalid as it does not match an existing object."
+  }
+
+  def "a contribution to an ordered configuration may only be overridden once"() {
+    buildRegistry FredModule, BarneyModule, ConfigurationOverrideModule, DuplicateConfigurationOverrideModule
+
+    def holder = getService "OrderedNames", NameListHolder
+
+    when:
+
+
+    holder.names
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Error invoking service contribution method"
+    e.message.contains "Contribution 'fred' has already been overridden"
+  }
+
+  def "contributions to mapped configurations may be overridden"() {
+    buildRegistry FredModule, BarneyModule, ConfigurationOverrideModule
+
+    when:
+
+    def sl = getService StringLookup
+
+    then:
+
+    sl.keys() == ["barney", "betty", "fred"]
+    sl.lookup("fred") == "Mr. Flintstone"
+  }
+
+  def "mapped configuration overrides must match an existing value"() {
+    buildRegistry FredModule, BarneyModule, NonmatchingMappedConfigurationOverrideModule
+
+    def sl = getService StringLookup
+
+    when:
+
+    sl.keys()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Override for key alley cat (at org.apache.tapestry5.ioc.util.NonmatchingMappedConfigurationOverrideModule.contributeStringLookup(MappedConfiguration)"
+    e.message.contains "does not match an existing key"
+  }
+
+  def "a contribution to a mapped configuration may only be overridden once"() {
+    buildRegistry FredModule, BarneyModule, ConfigurationOverrideModule, DuplicateConfigurationOverrideModule
+
+    def sl = getService StringLookup
+
+    when:
+
+    sl.keys()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Error invoking service contribution method"
+    e.message.contains "Contribution key fred has already been overridden"
+  }
+
+  def "support for @Contribute annotation"() {
+
+    buildRegistry AlphabetModule, AlphabetModule2
+
+    when:
+
+    def greek = getService "Greek", NameListHolder
+
+    then:
+
+    greek.names == ["Alpha", "Beta", "Gamma", "Delta"]
+
+    when:
+
+    def anotherGreek = getService "AnotherGreek", NameListHolder
+
+    then:
+
+    anotherGreek.names == ["Alpha", "Beta", "Gamma", "Delta", "Epsilon"]
+
+    when:
+
+    def hebrew = getService "Hebrew", NameListHolder
+
+    then:
+
+    hebrew.names == ["Alef", "Bet", "Gimel", "Dalet", "He", "Vav"]
+
+    when:
+
+    def holder = getService "ServiceWithEmptyConfiguration", NameListHolder2
+
+    then:
+
+    holder.names.empty
+  }
+
+  def "contribute by @Contribute annotation to non-existent service"() {
+    when:
+
+    buildRegistry InvalidContributeDefModule2
+
+    then:
+
+    RuntimeException e = thrown()
+
+    ["Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule2.provideConfiguration(OrderedConfiguration)",
+        "is for service 'interface org.apache.tapestry5.ioc.NameListHolder'",
+        "qualified with marker annotations [",
+        "interface org.apache.tapestry5.ioc.BlueMarker",
+        "interface org.apache.tapestry5.ioc.RedMarker",
+        "], which does not exist."].every { e.message.contains it}
+  }
+
+  def "contribute using @Contribute using invalid marker annotation is an exception"() {
+    when:
+    buildRegistry InvalidContributeDefModule3
+    then:
+    RuntimeException e = thrown()
+
+    ["Contribution org.apache.tapestry5.ioc.InvalidContributeDefModule3.provideConfiguration(OrderedConfiguration)",
+        "is for service 'interface org.apache.tapestry5.ioc.NameListHolder'",
+        "qualified with marker annotations [interface org.apache.tapestry5.ioc.BlueMarker], which does not exist."].every
+        { e.message.contains it}
+  }
+
+  def "ServiceResources are available to contribution methods"() {
+    buildRegistry InjectionCheckModule
+
+    when:
+
+    def s = getService InjectionCheck
+    def il = s.getValue "indirect-resources"
+
+    then:
+
+    s.logger.is(s.getValue("logger"))
+
+    s.logger.is il.logger
+    s.logger.is il.resources.logger
+  }
+
+
+  def "service id in contribute method is matched caselessly"() {
+    buildRegistry CaseInsensitiveContributeMethodModule
+
+    when:
+
+    def ss = getService SymbolSource
+
+    then:
+
+    ss.valueForSymbol("it") == "works"
+  }
+
+  def "contributed values may be coerced to the correct type"() {
+    buildRegistry ContributedValueCoercionModule
+
+    when:
+
+    def ss = getService SymbolSource
+
+    then:
+
+    ss.valueForSymbol("bool-true") == "true"
+    ss.valueForSymbol("bool-false") == "false"
+    ss.valueForSymbol("num-12345") == "12345"
+  }
+
+  def "@Optional contribution to an unknown service is not an error"() {
+    when:
+    buildRegistry OptionalContributionModule
+
+    then:
+    noExceptionThrown()
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ContributionDefImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ContributionDefImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ContributionDefImplSpec.groovy
new file mode 100644
index 0000000..e85c516
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ContributionDefImplSpec.groovy
@@ -0,0 +1,209 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.annotations.InjectService
+import org.apache.tapestry5.ioc.internal.ContributionDefImpl
+import org.apache.tapestry5.ioc.internal.QuietOperationTracker
+import org.apache.tapestry5.ioc.internal.UpcaseService
+import org.slf4j.Logger
+import spock.lang.Shared
+import spock.lang.Specification
+
+import java.lang.reflect.Method
+import javax.inject.Named
+
+import org.apache.tapestry5.ioc.*
+
+class ModuleFixture {
+
+  void contributeUnordered(Configuration configuration) {
+    configuration.add(ContributionDefImplSpec.toContribute);
+  }
+
+  void contributeUnorderedInjectedService(Configuration<UpcaseService> configuration,
+                                          @InjectService("Zap")
+                                          UpcaseService service) {
+    configuration.add(service);
+  }
+
+  void contributeUnorderedParameterNamedService(Configuration<UpcaseService> configuration,
+                                                @Named("Zap")
+                                                UpcaseService service) {
+    configuration.add(service);
+  }
+
+  void contributeUnorderedWrongParameter(MappedConfiguration configuration) {
+    throw new IllegalStateException("Unreachable.")
+  }
+
+  void contributeOrderedParameterInjectedService(OrderedConfiguration<UpcaseService> configuration,
+                                                 @InjectService("Zap")
+                                                 UpcaseService service) {
+    configuration.add("fred", service);
+  }
+
+  void contributeMappedParameterInjectedService(MappedConfiguration<String, UpcaseService> configuration,
+                                                @InjectService("Zap")
+                                                UpcaseService service) {
+    configuration.add("upcase", service);
+  }
+}
+
+
+class ContributionDefImplSpec extends Specification {
+
+  static Object toContribute
+
+  @Shared
+  OperationTracker tracker = new QuietOperationTracker()
+
+  ServiceResources resources = Mock()
+  Logger logger = Mock()
+
+  @Shared
+  ModuleBuilderSource source = new ModuleBuilderSource() {
+
+    @Override
+    Object getModuleBuilder() {
+      return new ModuleFixture()
+    }
+  }
+
+  private Method findMethod(name) {
+    return ModuleFixture.methods.find { it.name == name }
+  }
+
+  def createContributionDef(methodName) {
+    return new ContributionDefImpl("Foo", findMethod(methodName), false, null, null, null)
+  }
+
+  def "contribute to an unordered collection"() {
+
+    Configuration configuration = Mock()
+
+    toContribute = new Object()
+
+    def cd = createContributionDef "contributeUnordered"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    1 * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    1 * configuration.add(toContribute)
+    _ * resources.tracker >> tracker
+
+    0 * _
+  }
+
+  def "unordered configuration injects and contributes a service via @InjectService"() {
+
+    Configuration configuration = Mock()
+    UpcaseService service = Mock()
+
+    def cd = createContributionDef "contributeUnorderedInjectedService"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    1 * resources.getService("Zap", UpcaseService) >> service
+
+    1 * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    1 * configuration.add(service)
+    _ * resources.tracker >> tracker
+
+    0 * _
+  }
+
+  def "unordered configuration injects and contributes a service via @Named"() {
+    Configuration configuration = Mock()
+    UpcaseService service = Mock()
+
+    def cd = createContributionDef "contributeUnorderedParameterNamedService"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    1 * resources.getService("Zap", UpcaseService) >> service
+
+    1 * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    1 * configuration.add(service)
+    _ * resources.tracker >> tracker
+
+  }
+
+  def "contribution method configuration parameter must be correct type"() {
+    Configuration configuration = Mock()
+
+    def cd = createContributionDef "contributeUnorderedWrongParameter"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    _ * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    _ * resources.tracker >> tracker
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Service 'Foo' is configured using org.apache.tapestry5.ioc.Configuration, not org.apache.tapestry5.ioc.MappedConfiguration."
+  }
+
+  def "ordered configuration injects and contributes a service via @InjectService"() {
+
+    OrderedConfiguration configuration = Mock()
+    UpcaseService service = Mock()
+
+    def cd = createContributionDef "contributeOrderedParameterInjectedService"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    1 * configuration.add("fred", service)
+
+    _ * resources.getService("Zap", UpcaseService) >> service
+
+    _ * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    _ * resources.tracker >> tracker
+  }
+
+  def "mapped configuration injects and contributes a service via @InjectService"() {
+    MappedConfiguration configuration = Mock()
+    UpcaseService service = Mock()
+
+    def cd = createContributionDef "contributeMappedParameterInjectedService"
+
+    when:
+
+    cd.contribute(source, resources, configuration)
+
+    then:
+
+    1 * configuration.add("upcase", service)
+
+    _ * resources.getService("Zap", UpcaseService) >> service
+
+    _ * resources.logger >> logger
+    _ * resources.serviceId >> "Foo"
+    _ * resources.tracker >> tracker
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/CronExpressionSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/CronExpressionSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/CronExpressionSpec.groovy
new file mode 100644
index 0000000..58bef3e
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/CronExpressionSpec.groovy
@@ -0,0 +1,104 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.services.cron.CronExpression
+import spock.lang.Ignore
+import spock.lang.Specification
+import spock.lang.Unroll
+
+import java.text.ParseException
+
+@Unroll
+class CronExpressionSpec extends Specification {
+
+  // This allows the any of the constants defined on Calendar to be used
+  // without qualification.
+  def propertyMissing(String name) { Calendar[name] }
+
+  def "isSatisfiedBy(#year, #month, #day, #hour, #minute, #second ) should be #satisfied for expression '#expr'"() {
+
+    def cal = Calendar.getInstance();
+
+    def exp = new CronExpression(expr)
+
+    cal.set year, month, day, hour, minute, second
+
+    expect:
+
+    exp.isSatisfiedBy(cal.time) == satisfied
+
+    where:
+    expr                    | year | month   | day | hour | minute | second | satisfied
+    "0 15 10 * * ? 2005"    | 2005 | JUNE    | 1   | 10   | 15     | 0      | true
+    "0 15 10 * * ? 2005"    | 2006 | JUNE    | 1   | 10   | 15     | 0      | false
+    "0 15 10 * * ? 2005"    | 2005 | JUNE    | 1   | 10   | 16     | 0      | false
+    "0 15 10 * * ? 2005"    | 2005 | JUNE    | 1   | 10   | 14     | 0      | false
+    "0 15 10 L-2 * ? 2010"  | 2010 | OCTOBER | 29  | 10   | 15     | 0      | true
+    "0 15 10 L-2 * ? 2010"  | 2010 | OCTOBER | 28  | 10   | 15     | 0      | false
+    "0 15 10 L-5W * ? 2010" | 2010 | OCTOBER | 26  | 10   | 15     | 0      | true
+    "0 15 10 L-1 * ? 2010"  | 2010 | OCTOBER | 30  | 10   | 15     | 0      | true
+    "0 15 10 L-1W * ? 2010" | 2010 | OCTOBER | 29  | 10   | 15     | 0      | true
+  }
+
+  def cloneViaSerialize(obj) {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream()
+
+    baos.withObjectOutputStream { it.writeObject(obj) }
+
+    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray())
+    ObjectInputStream ois = new ObjectInputStream(bais)
+
+    ois.readObject()
+  }
+
+  // This test is in the original TestNG test but failed there (making me think that the test case was probably
+  // not being run). It's clear that CronExpressions do not deserialize correctly by looking at the source!
+  @Ignore
+  def "check that CronExpressions serialize and deserialize"() {
+
+    CronExpression original = new CronExpression("19 15 10 4 Apr ? ")
+
+    when:
+
+    CronExpression cloned = cloneViaSerialize original
+
+    then:
+
+    cloned.cronExpression == original.cronExpression
+    cloned.getNextValidTimeAfter(new Date()) != null
+  }
+
+  def "Parse failure: parse of '#expr' should fail with '#err'"() {
+
+    when:
+    new CronExpression(expr)
+
+    then:
+    def e = thrown(ParseException)
+
+    assert e.message.startsWith(err)
+
+    where:
+    expr                   | err
+    "* * * * Foo ? "       | "Invalid Month value:"
+    "* * * * Jan-Foo ? "   | "Invalid Month value:"
+    "0 0 * * * *"          | "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."
+    "0 0 * 4 * *"          | "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."
+    "0 0 * * * 4"          | "Support for specifying both a day-of-week AND a day-of-month parameter is not implemented."
+    "0 43 9 1,5,29,L * ?"  | "Support for specifying 'L' and 'LW' with other days of the month is not implemented"
+    "0 43 9 ? * SAT,SUN,L" | "Support for specifying 'L' with other days of the week is not implemented"
+    "0 43 9 ? * 6,7,L"     | "Support for specifying 'L' with other days of the week is not implemented"
+    "0/5 * * 32W 1 ?"      | "The 'W' option does not make sense with values larger than"
+  }
+
+  def "Expression '#expr' is valid"() {
+    when:
+    new CronExpression(expr)
+
+    then:
+    noExceptionThrown()
+
+    where:
+    expr << ["0 43 9 ? * 5L"]
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/CronScheduleSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/CronScheduleSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/CronScheduleSpec.groovy
new file mode 100644
index 0000000..620fa89
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/CronScheduleSpec.groovy
@@ -0,0 +1,26 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.cron.CronSchedule
+import org.apache.tapestry5.ioc.services.cron.PeriodicExecutor
+
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+class CronScheduleSpec extends AbstractSharedRegistrySpecification {
+
+  def "add a job and ensure that it executes"() {
+    def latch = new CountDownLatch(5)
+
+    def executor = getService PeriodicExecutor
+
+    executor.addJob(new CronSchedule("0/1 * * * * ?"), "Test", { latch.countDown() })
+
+    when:
+
+    latch.await(30, TimeUnit.SECONDS)
+
+    then:
+
+    latch.count == 0
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/DecoratorsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/DecoratorsSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/DecoratorsSpec.groovy
new file mode 100644
index 0000000..457988c
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/DecoratorsSpec.groovy
@@ -0,0 +1,110 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.DecorateByMarkerModule
+import org.apache.tapestry5.ioc.internal.DecorateByMarkerModule2
+import org.apache.tapestry5.ioc.*
+
+/** Integration tests for service decorators and some related behaviors. */
+class DecoratorsSpec extends AbstractRegistrySpecification {
+
+  def "verify order of service decorators"() {
+    buildRegistry FredModule, BarneyModule
+
+    def fred = getService "Fred", Runnable
+    def list = getService DecoratorList
+
+    when:
+
+    fred.run()
+
+    then:
+
+    list.names == ["gamma", "beta", "alpha"]
+  }
+
+  def "decorators receive the delegate by the specific type"() {
+
+    buildRegistry GreeterModule, SpecificDecoratorModule
+
+    when:
+
+    def g = getService "HelloGreeter", Greeter
+
+    then:
+
+    g.greeting == "HELLO"
+  }
+
+  def "a service builder method with @PreventServiceDecoration is not decorated"() {
+    buildRegistry PreventDecorationModule
+
+    when:
+
+    def st = getService StringTransformer
+
+    then:
+
+    st.transform("tapestry") == "TAPESTRY"
+  }
+
+  def "Binding a service with explicit no decorations will ensure that the implementation is not decorated"() {
+    buildRegistry PreventDecorationModule
+
+    when:
+
+    def g = getService Greeter
+
+    then:
+
+    g.greeting == "Greetings from ServiceIdGreeter."
+  }
+
+  def "@PreventServiceDecoration on a service implementation class ensures that the implementation is not decorated"() {
+    buildRegistry PreventDecorationModule
+
+    when:
+
+    def rocket = getService Rocket
+
+    then:
+
+    rocket.countdown == "3, 2, 1, Launch!"
+  }
+
+  def "@Decorate marks a module method as a decorator method"() {
+    buildRegistry GreeterModule2, DecorateByMarkerModule
+
+    when:
+
+    def green = getService "GreenGreeter", Greeter
+
+    then:
+
+    green.greeting == "Decorated by foo[Decorated by baz[Decorated by bar[Green]]]"
+  }
+
+  def "@Decorate with @Local only decorates services from the same module"() {
+    buildRegistry GreeterModule2, DecorateByMarkerModule
+
+    when:
+
+    def red = getService "RedGreeter", Greeter
+
+    then:
+
+    red.greeting == "Decorated by barney[Red]"
+  }
+
+  def "@Decorate with id attribute"() {
+    buildRegistry DecorateByMarkerModule2
+
+    when:
+
+    def green = getService "RedGreeter", Greeter
+
+    then:
+
+    green.greeting == "Decorated by beta[Decorated by alpha[Red]]"
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/DefaultImplementationBuilderImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/DefaultImplementationBuilderImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/DefaultImplementationBuilderImplSpec.groovy
new file mode 100644
index 0000000..95d2a2e
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/DefaultImplementationBuilderImplSpec.groovy
@@ -0,0 +1,39 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.DefaultImplementationBuilder
+
+
+class DefaultImplementationBuilderImplSpec extends AbstractSharedRegistrySpecification {
+
+  DefaultImplementationBuilder builder = getService(DefaultImplementationBuilder)
+
+  def "default simple interface does nothing"() {
+    Runnable r = builder.createDefaultImplementation(Runnable)
+
+    when:
+
+    r.run()
+
+    then:
+
+    assert r.toString() == "<NoOp java.lang.Runnable>"
+  }
+
+  def "when toString() is part of interface, the default returns null"() {
+    ToString ts = builder.createDefaultImplementation(ToString)
+
+    expect:
+
+    ts.toString() == null
+  }
+
+  def "built instances are cached (by type)"() {
+    Runnable r1 = builder.createDefaultImplementation(Runnable)
+    Runnable r2 = builder.createDefaultImplementation(Runnable)
+
+    expect:
+
+    r1.is r2
+  }
+
+}