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/05/16 20:50:31 UTC

[18/44] git commit: Convert TestNG to Spock

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/cf890643
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/cf890643
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/cf890643

Branch: refs/heads/master
Commit: cf89064355ece3f9a2392501649d0777a839923b
Parents: d49dd96
Author: Howard M. Lewis Ship <hl...@gmail.com>
Authored: Thu May 3 10:38:45 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed May 16 11:50:13 2012 -0700

----------------------------------------------------------------------
 .../org/apache/tapestry5/ioc/AdvisorsSpec.groovy   |   83 +
 .../org/apache/tapestry5/ioc/AutobuildSpec.groovy  |  171 ++
 .../apache/tapestry5/ioc/ConfigurationsSpec.groovy |  316 +++
 .../org/apache/tapestry5/ioc/DecoratorsSpec.groovy |  109 +
 .../org/apache/tapestry5/ioc/InjectionSpec.groovy  |  123 ++
 .../tapestry5/ioc/ManifestProcessingSpec.groovy    |   36 +
 .../tapestry5/ioc/ModuleInstantiationSpec.groovy   |   52 +
 .../apache/tapestry5/ioc/PerThreadScopeSpec.groovy |   88 +
 .../tapestry5/ioc/RegistryBuilderSpec.groovy       |    3 -
 ...RegistryConstructionAndRuntimeErrorsSpec.groovy |  117 +
 .../org/apache/tapestry5/ioc/RegistrySpec.groovy   |   53 +
 .../ioc/ServiceActivityScoreboardSpec.groovy       |   74 +
 .../apache/tapestry5/ioc/ServiceBinderSpec.groovy  |   35 +
 .../apache/tapestry5/ioc/ServiceLookupSpec.groovy  |  116 +
 .../apache/tapestry5/ioc/ServiceProxySpec.groovy   |   96 +
 .../org/apache/tapestry5/ioc/BarneyModule.java     |  180 +-
 .../apache/tapestry5/ioc/CountingGreeterImpl.java  |    4 +-
 .../org/apache/tapestry5/ioc/IntegrationTest.java  | 1629 ---------------
 .../tapestry5/ioc/IntegrationTestFixture.java      |    9 +
 .../tapestry5/ioc/RecursiveConstructorModule.java  |    5 +-
 .../org/apache/tapestry5/ioc/StaticModule.java     |  179 +-
 .../apache/tapestry5/ioc/StringTransformer.java    |    6 +-
 22 files changed, 1652 insertions(+), 1832 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AdvisorsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AdvisorsSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AdvisorsSpec.groovy
new file mode 100644
index 0000000..d4abc93
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AdvisorsSpec.groovy
@@ -0,0 +1,83 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.internal.AdviseByMarkerModule
+import org.apache.tapestry5.ioc.internal.AdviseByMarkerModule2
+
+
+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/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AutobuildSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AutobuildSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AutobuildSpec.groovy
new file mode 100644
index 0000000..d5aa6a5
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AutobuildSpec.groovy
@@ -0,0 +1,171 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.internal.ExceptionInConstructorModule
+
+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/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ConfigurationsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ConfigurationsSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ConfigurationsSpec.groovy
new file mode 100644
index 0000000..d0db942
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ConfigurationsSpec.groovy
@@ -0,0 +1,316 @@
+package org.apache.tapestry5.ioc
+
+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
+
+/** 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/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/DecoratorsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/DecoratorsSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/DecoratorsSpec.groovy
new file mode 100644
index 0000000..8dfa727
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/DecoratorsSpec.groovy
@@ -0,0 +1,109 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.internal.DecorateByMarkerModule
+import org.apache.tapestry5.ioc.internal.DecorateByMarkerModule2
+
+/** 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/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/InjectionSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/InjectionSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/InjectionSpec.groovy
new file mode 100644
index 0000000..33650f9
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/InjectionSpec.groovy
@@ -0,0 +1,123 @@
+package org.apache.tapestry5.ioc
+
+
+class InjectionSpec extends AbstractRegistrySpecification {
+
+  def "symbol in @Inject is expanded"() {
+
+    buildRegistry GreeterModule
+
+    when:
+
+    def greeter = getService "Greeter", Greeter
+
+    then:
+
+    greeter.greeting == "Hello"
+    greeter.toString() == "<Proxy for Greeter(org.apache.tapestry5.ioc.Greeter)>"
+  }
+
+  def "injection by marker with single match"() {
+
+    buildRegistry GreeterModule
+
+    when:
+
+    def greeter = getService "InjectedBlueGreeter", Greeter
+
+    then:
+
+    greeter.greeting == "Blue"
+  }
+
+  def "verify exception for inject by marker with multiple matches"() {
+    buildRegistry GreeterModule
+
+    def greeter = getService "InjectedRedGreeter", Greeter
+
+    when:
+
+    greeter.greeting
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Unable to locate a single service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.RedMarker"
+    e.message.contains "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter1()"
+    e.message.contains "org.apache.tapestry5.ioc.GreeterModule.buildRedGreeter2()"
+  }
+
+  def "verify exception for injection by marker and no matches"() {
+    buildRegistry GreeterModule
+
+    def greeter = getService "InjectedYellowGreeter", Greeter
+
+    when:
+
+    greeter.greeting
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Exception constructing service"
+    e.message.contains "Unable to locate any service assignable to type org.apache.tapestry5.ioc.Greeter with marker annotation(s) org.apache.tapestry5.ioc.YellowMarker."
+  }
+
+  def "recursion handling injections (due to MasterObjectProvider) is detected"() {
+
+    buildRegistry CyclicMOPModule
+
+    def trigger = getService "Trigger", Runnable
+
+    when:
+
+    trigger.run()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Construction of service 'TypeCoercer' has failed due to recursion"
+  }
+
+  def "A field may be annotated with @InjectResource to receive resources"() {
+
+    buildRegistry FieldResourceInjectionModule
+
+    when:
+
+    def s = getService FieldResourceService
+
+    then:
+
+    s.serviceId == "FieldResourceService"
+
+    s.labels == ["Barney", "Betty", "Fred", "Wilma"]
+  }
+
+  def "methods with @PostInjection are invoked and can be passed further injections"() {
+    buildRegistry PostInjectionMethodModule
+
+    when:
+
+    def g = getService Greeter
+
+    then:
+
+    g.greeting == "Greetings from ServiceIdGreeter."
+  }
+
+  def "a service may be overridden by contributing to ServiceOverride"() {
+    buildRegistry GreeterServiceOverrideModule
+
+    when:
+
+    def g = getObject Greeter, null
+
+    then:
+
+    g.greeting == "Override Greeting"
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ManifestProcessingSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ManifestProcessingSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ManifestProcessingSpec.groovy
new file mode 100644
index 0000000..51b0949
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ManifestProcessingSpec.groovy
@@ -0,0 +1,36 @@
+package org.apache.tapestry5.ioc
+
+import spock.lang.Specification
+
+
+class ManifestProcessingSpec extends Specification {
+
+  def "invalid class in manifest"() {
+
+    File fakejar = new File("src/test/fakejar")
+
+    expect:
+
+      // This is more to verify the module execution environment
+      fakejar.exists()
+      fakejar.isDirectory()
+
+    when:
+
+    URL url = fakejar.toURL()
+    URLClassLoader loader = new URLClassLoader([url] as URL[], Thread.currentThread().contextClassLoader)
+
+    RegistryBuilder builder = new RegistryBuilder(loader)
+
+    IOCUtilities.addDefaultModules(builder)
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Exception loading module(s) from manifest"
+    e.message.contains "Failure loading Tapestry IoC module class does.not.exist.Module"
+
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ModuleInstantiationSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ModuleInstantiationSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ModuleInstantiationSpec.groovy
new file mode 100644
index 0000000..67c2fb3
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ModuleInstantiationSpec.groovy
@@ -0,0 +1,52 @@
+package org.apache.tapestry5.ioc
+
+
+class ModuleInstantiationSpec extends AbstractRegistrySpecification {
+  def setup() {
+    StaticModule.reset()
+  }
+
+  def "module class is not instantiated when invoking static builder method"() {
+    buildRegistry StaticModule
+
+    def fred = getService "Fred", Runnable
+
+    when:
+
+    fred.run()
+
+    then:
+
+    !StaticModule.instantiated
+    StaticModule.fredRan
+  }
+
+  def "module class is not instantiated when invoking static decorator method"() {
+    buildRegistry StaticModule
+
+    def barney = getService "Barney", Runnable
+
+    when:
+
+    barney.run()
+
+    then:
+
+    !StaticModule.instantiated
+    StaticModule.decoratorRan
+  }
+
+  def "module class is not instantiated when invoking a static contributor method"() {
+    buildRegistry StaticModule
+
+    def holder = getService "Names", NameListHolder
+
+    when:
+
+    assert holder.names == ["Fred"]
+
+    then:
+
+    !StaticModule.instantiated
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/PerThreadScopeSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/PerThreadScopeSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/PerThreadScopeSpec.groovy
new file mode 100644
index 0000000..9533862
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/PerThreadScopeSpec.groovy
@@ -0,0 +1,88 @@
+package org.apache.tapestry5.ioc
+
+
+class PerThreadScopeSpec extends AbstractRegistrySpecification {
+
+  def "ensure that different threads see different implementations"() {
+    def threadExecuted = false
+
+    buildRegistry PerThreadModule
+
+    def holder = getService StringHolder
+
+    when:
+
+    holder.value = "fred"
+
+    then:
+
+    holder.value == "fred"
+
+    when:
+
+    Thread t = new Thread({
+      assert holder.value == null
+
+      holder.value = "barney"
+
+      assert holder.value == "barney"
+
+      threadExecuted = true
+
+      cleanupThread()
+    })
+
+    t.start()
+    t.join()
+
+    then:
+
+    threadExecuted
+    holder.value == "fred"
+  }
+
+  def "services with out a service interface must use the default scope"() {
+
+    buildRegistry ScopeMismatchModule
+
+    when:
+
+    getService StringBuilder
+
+    then:
+
+    Exception e = thrown()
+
+    e.message.contains "Error building service proxy for service 'ScopeRequiresAProxyAndNoInterfaceIsProvided'"
+    e.message.contains "Service scope 'perthread' requires a proxy"
+  }
+
+  def "ensure that perthread services are discarded by cleanupThread()"() {
+    buildRegistry PerThreadModule
+
+    when:
+
+    def holder = getService StringHolder
+
+    then:
+
+    holder.value == null
+
+    when:
+
+    holder.value = "fred"
+
+    then:
+
+    holder.value == "fred"
+
+    when:
+
+    cleanupThread()
+
+    then:
+
+    holder.value == null
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy
index 5fb91b7..98db472 100644
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy
@@ -11,9 +11,6 @@ class RegistryBuilderSpec extends Specification {
     def "@SubModule annotation is honored"() {
         when:
 
-        // Borrowed from IntegrationTest, this will only work if both FredModule and BarneyModule
-        // are loaded.
-
         Registry r = new RegistryBuilder().add(MasterModule).build()
 
         def service = r.getService("UnorderedNames", NameListHolder)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryConstructionAndRuntimeErrorsSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryConstructionAndRuntimeErrorsSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryConstructionAndRuntimeErrorsSpec.groovy
new file mode 100644
index 0000000..70da305
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryConstructionAndRuntimeErrorsSpec.groovy
@@ -0,0 +1,117 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.internal.ExtraPublicConstructorsModule
+import org.apache.tapestry5.ioc.internal.PrivateConstructorModule
+import org.apache.tapestry5.ioc.internal.UpcaseService
+
+/**
+ * A few tests that are easiest (or even just possible) by building a Registry and trying out a few
+ * things.
+ */
+class RegistryConstructionAndRuntimeErrorsSpec extends AbstractRegistrySpecification {
+
+  def "duplicate service names are a failure"() {
+    when:
+
+    buildRegistry FredModule, DuplicateFredModule
+
+    then:
+
+    RuntimeException ex = thrown()
+
+    ex.message.startsWith "Service id 'Fred' has already been defined by"
+
+    // Can't check entire message, can't guarantee what order modules will be processed in
+  }
+
+  def "service with unknown scope fails at service proxy creation"() {
+    buildRegistry UnknownScopeModule
+
+    when:
+
+    getService "UnknownScope", Runnable
+
+    then:
+
+    Exception e = thrown()
+
+    e.message.contains "Error building service proxy for service 'UnknownScope'"
+    e.message.contains "Unknown service scope 'magic'"
+  }
+
+  def "ensure that recursive module construction is detected"() {
+
+    buildRegistry RecursiveConstructorModule
+
+    def runnable = getService "Runnable", Runnable
+
+    when:
+
+    // We can get the proxy, but invoking a method causes
+    // the module to be instantiated ... but that also invokes a method on
+    // the proxy.
+
+    runnable.run()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "has failed due to recursion"
+  }
+
+  def "a module class must have a public constructor"() {
+
+    buildRegistry PrivateConstructorModule
+
+    def trigger = getService "Trigger", Runnable
+
+    when:
+
+    trigger.run()
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Module class org.apache.tapestry5.ioc.internal.PrivateConstructorModule does not contain any public constructors."
+  }
+
+  def "extra public constructors on a module class are ignored"() {
+
+    buildRegistry ExtraPublicConstructorsModule
+
+    when: "forcing the module to be instantiated"
+
+    def upcase = getService UpcaseService
+
+    then: "no exception when instantiating the module"
+
+    upcase.upcase('Hello, ${fred}') == "HELLO, FLINTSTONE"
+  }
+
+  def "extra public methods on module classes are exceptions"() {
+    when:
+    buildRegistry ExtraMethodsModule
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Module class org.apache.tapestry5.ioc.ExtraMethodsModule contains unrecognized public methods: "
+    e.message.contains "thisMethodIsInvalid()"
+    e.message.contains "soIsThisMethod()"
+  }
+
+  def "can not use withSimpleId() when binding a service interface to a ServiceBuilder callback"() {
+    when:
+
+    buildRegistry NoImplementationClassForSimpleIdModule
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "No defined implementation class to generate simple id from"
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistrySpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistrySpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistrySpec.groovy
new file mode 100644
index 0000000..d8432cc
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistrySpec.groovy
@@ -0,0 +1,53 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.internal.services.StartupModule2
+
+class RegistrySpec extends AbstractRegistrySpecification {
+
+  def "symbol in Registry.getService() is expanded"() {
+
+    buildRegistry GreeterModule
+
+    when:
+
+    def greeter = getService '${greeter}', Greeter
+
+    then:
+
+    greeter.greeting == "Hello"
+    greeter.toString() == "<Proxy for HelloGreeter(org.apache.tapestry5.ioc.Greeter)>"
+  }
+
+  def "circular module references are ignored"() {
+    buildRegistry HelterModule
+
+    when:
+
+    def helter = getService "Helter", Runnable
+    def skelter = getService "Skelter", Runnable
+
+    then:
+
+    !helter.is(skelter)
+  }
+
+  def "@Startup annotation support"() {
+    when:
+
+    buildRegistry StartupModule2
+
+    then:
+
+    !StartupModule2.staticStartupInvoked
+    !StartupModule2.instanceStartupInvoked
+
+    when:
+
+    performRegistryStartup()
+
+    then:
+
+    StartupModule2.staticStartupInvoked
+    StartupModule2.instanceStartupInvoked
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceActivityScoreboardSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceActivityScoreboardSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceActivityScoreboardSpec.groovy
new file mode 100644
index 0000000..98a4914
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceActivityScoreboardSpec.groovy
@@ -0,0 +1,74 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.services.ServiceActivityScoreboard
+import org.apache.tapestry5.ioc.services.Status
+import org.apache.tapestry5.ioc.services.TypeCoercer
+
+class ServiceActivityScoreboardSpec extends AbstractRegistrySpecification {
+
+  def "general cursory test"() {
+    buildRegistry GreeterModule
+
+    def scoreboard = getService ServiceActivityScoreboard
+
+    when:
+
+    def tc = getService TypeCoercer
+
+    tc.coerce "123", Integer
+
+    getService "BlueGreeter", Greeter
+
+    then:
+
+    def activity = scoreboard.serviceActivity
+
+    !activity.empty
+
+    activity.find({ it.serviceId == "TypeCoercer" }).status == Status.REAL
+
+    def ppf = activity.find { it.serviceId == "PlasticProxyFactory" }
+    ppf.status == Status.BUILTIN
+
+
+    def rg = activity.find { it.serviceId == "RedGreeter1" }
+    rg.status == Status.DEFINED
+    rg.markers.contains RedMarker
+    !rg.markers.contains(BlueMarker)
+
+    def bg = activity.find { it.serviceId == "BlueGreeter"}
+
+    bg.status == Status.VIRTUAL
+    bg.markers.contains BlueMarker
+    !bg.markers.contains(RedMarker)
+  }
+
+  def "scoreboard entry for perthread services is itself perthread"() {
+
+    buildRegistry GreeterModule, PerThreadModule
+
+    def scoreboard = getService ServiceActivityScoreboard
+
+    def holder = getService StringHolder
+
+    when:
+
+    Thread t = new Thread({
+      holder.value = "barney"
+      assert holder.value == "barney"
+
+      assert scoreboard.serviceActivity.find({ it.serviceId == "StringHolder"}).status == Status.REAL
+
+      cleanupThread()
+    })
+
+    t.start()
+    t.join()
+
+    then:
+
+    scoreboard.serviceActivity.find({ it.serviceId == "StringHolder"}).status == Status.VIRTUAL
+
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceBinderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceBinderSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceBinderSpec.groovy
new file mode 100644
index 0000000..30448fc
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceBinderSpec.groovy
@@ -0,0 +1,35 @@
+package org.apache.tapestry5.ioc
+
+
+class ServiceBinderSpec extends AbstractRegistrySpecification {
+
+  def "a service implementation may be created via a ServiceBuilder callback"() {
+    buildRegistry ServiceBuilderModule
+
+    when:
+
+    def g = getService "Greeter", Greeter
+
+    then:
+    g.greeting == "Greetings from service Greeter."
+  }
+
+  def "verify exception reporting for ServiceBuilder that throws an exception"() {
+
+    buildRegistry ServiceBuilderModule
+
+    def g = getService "BrokenGreeter", Greeter
+
+    when:
+
+    g.greeting
+
+    then:
+
+    Exception e = thrown()
+
+    e.message.contains "Exception constructing service 'BrokenGreeter'"
+    e.message.contains "Failure inside ServiceBuilder callback."
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceLookupSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceLookupSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceLookupSpec.groovy
new file mode 100644
index 0000000..5a9828a
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceLookupSpec.groovy
@@ -0,0 +1,116 @@
+package org.apache.tapestry5.ioc
+
+import org.apache.tapestry5.ioc.services.Builtin
+import org.apache.tapestry5.ioc.services.TypeCoercer
+
+import java.sql.PreparedStatement
+
+class ServiceLookupSpec extends AbstractRegistrySpecification {
+
+  def "access to services by id is case insensitive"() {
+
+    buildRegistry FredModule
+
+    when:
+
+    def r1 = getService "Fred", Runnable
+    def r2 = getService "FRED", Runnable
+
+    then:
+
+    r1.is r2
+
+  }
+
+  def "verify exception when accessing service by unknown id"() {
+
+    buildRegistry()
+
+    when:
+
+    getService "PeekABoo", Runnable
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Service id 'PeekABoo' is not defined by any module."
+  }
+
+  def "verify exception when accessing a service by type and there are no matching services"() {
+
+    buildRegistry()
+
+    when:
+
+    getService PreparedStatement
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == "No service implements the interface java.sql.PreparedStatement."
+  }
+
+  def "verify exception when accessing a service by type and there are multiple matches"() {
+    buildRegistry DuplicateServiceTypeModule
+
+    when:
+
+    getService Pingable
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == "Service interface org.apache.tapestry5.ioc.Pingable is matched by 2 services: Barney, Fred.  Automatic dependency resolution requires that exactly one service implement the interface."
+  }
+
+  def "access to builtin service via marker annotation"() {
+    Builtin annotation = Mock()
+    AnnotationProvider ap = Mock()
+
+    buildRegistry()
+
+    def tc1 = getService "TypeCoercer", TypeCoercer
+
+    when:
+
+    def tc2 = getObject TypeCoercer, ap
+
+    then:
+
+    1 * ap.getAnnotation(Builtin) >> annotation
+
+    tc2.is tc1
+  }
+
+  def "lookup service by type and markers"() {
+    buildRegistry GreeterModule
+
+    when:
+
+    def blue = getService Greeter, BlueMarker
+
+    then:
+
+    blue.greeting == "Blue"
+  }
+
+  def "use of the @Local annotation disambiguates from multiple services"() {
+    buildRegistry GreeterModule, LocalModule
+
+
+    when:
+
+    // The implementation of this class relies on a @Local injection to find
+    // a specific Greeter implementation
+
+    StringHolder holder = getService "LocalGreeterHolder", StringHolder
+
+    then:
+
+    holder.value == "Hello, y'all!"
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceProxySpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceProxySpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceProxySpec.groovy
new file mode 100644
index 0000000..9222792
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceProxySpec.groovy
@@ -0,0 +1,96 @@
+package org.apache.tapestry5.ioc
+
+class ServiceProxySpec extends AbstractRegistrySpecification {
+
+  def "shutdown deactivaties proxies"() {
+    buildRegistry FredModule, BarneyModule
+
+    def fred = getService "Fred", Runnable
+
+    fred.run()
+
+    shutdown()
+
+    when:
+
+    fred.run()
+
+    then:
+
+    RuntimeException ex = thrown()
+
+    ex.message.contains "Proxy for service Fred is no longer active because the IOC Registry has been shut down."
+
+    fred.toString() == "<Proxy for Fred(java.lang.Runnable)>"
+
+    cleanup:
+
+    registry = null
+  }
+
+  def "show that services defined without a implementation are instantiated immediately"() {
+    buildRegistry NonProxiedServiceModule
+
+    when:
+
+    def holder = getService StringHolder
+
+    then:
+
+    holder instanceof StringHolderImpl // and not some proxy
+  }
+
+  def "service builder methods with a class (not interface) return type are not proxied, but are cached"() {
+
+    buildRegistry ConcreteServiceBuilderModule
+
+    when:
+
+    def h1 = getService StringHolder
+
+    then: "not proxied"
+
+    h1 instanceof StringHolderImpl
+
+    when:
+
+    def h2 = getService StringHolder
+
+    then: "cached"
+
+    h2.is h1
+  }
+
+  def "verify that a proxy for an autobuilt object lazily instantiates the implementation"() {
+
+    buildRegistry()
+
+    expect:
+    IntegrationTestFixture.countingGreeterInstantiationCount == 0
+
+    when: "obtaining the proxy"
+
+    def g = proxy Greeter, CountingGreeterImpl
+
+    then: "the implementation is not yet instantiated"
+
+    IntegrationTestFixture.countingGreeterInstantiationCount == 0
+
+    when: "invoking toString() on the proxy"
+
+    assert g.toString() == "<Autoreload proxy org.apache.tapestry5.ioc.CountingGreeterImpl(org.apache.tapestry5.ioc.Greeter)>"
+
+    then: "the implementation is not yet instantiated"
+
+    IntegrationTestFixture.countingGreeterInstantiationCount == 0
+
+    when: "invoking other methods on the proxy"
+
+    assert g.greeting == "Hello"
+
+    then: "the implementation is now instantiated"
+
+    IntegrationTestFixture.countingGreeterInstantiationCount == 1
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/BarneyModule.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/BarneyModule.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/BarneyModule.java
index 7969317..eeb143b 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/BarneyModule.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/BarneyModule.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 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.
@@ -14,12 +14,12 @@
 
 package org.apache.tapestry5.ioc;
 
+import org.apache.tapestry5.ioc.annotations.Contribute;
 import org.apache.tapestry5.ioc.annotations.Match;
 import org.apache.tapestry5.ioc.annotations.Order;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.services.StrategyBuilder;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -27,94 +27,88 @@ import java.util.Map;
  * Module used to demonstrate decorator ordering.
  */
 
-public class BarneyModule
-{
-    @Match(
-            {"UnorderedNames", "Fred", "PrivateFredAlias"})
-    @Order("after:Beta")
-    public Object decorateGamma(Object delegate, DecoratorList list)
-    {
-        list.add("gamma");
-
-        return null;
-    }
-
-    public Sizer buildSizer(final Map<Class, Sizer> configuration)
-    {
-        return new Sizer()
-        {
-            public int size(Object object)
-            {
-                if (object == null) return 0;
-
-                Sizer sizer = configuration.get(object.getClass());
-
-                if (sizer != null) return sizer.size(object);
-
-                return 1;
-            }
-        };
-    }
-
-    public void contributeSizer(MappedConfiguration<Class, Sizer> configuration)
-    {
-        Sizer listSizer = new Sizer()
-        {
-            public int size(Object object)
-            {
-                List list = (List) object;
-
-                return list.size();
-            }
-        };
-
-        Sizer mapSizer = new Sizer()
-        {
-            public int size(Object object)
-            {
-                Map map = (Map) object;
-
-                return map.size();
-            }
-        };
-
-        // Have to work on concrete class, rather than type, until we move the StrategyFactory
-        // over from HiveMind.
-
-        configuration.add(ArrayList.class, listSizer);
-        configuration.add(HashMap.class, mapSizer);
-    }
-
-    /**
-     * Put DecoratorList in module barney, where so it won't accidentally be decorated (which recusively builds the
-     * service, and is caught as a failure).
-     */
-    public DecoratorList buildDecoratorList()
-    {
-        return new DecoratorList()
-        {
-            private List<String> names = CollectionFactory.newList();
-
-            public void add(String name)
-            {
-                names.add(name);
-            }
-
-            public List<String> getNames()
-            {
-                return names;
-            }
-        };
-    }
-
-    public void contributeUnorderedNames(Configuration<String> configuration)
-    {
-        configuration.add("Gamma");
-    }
-
-    public void contributeStringLookup(MappedConfiguration<String, String> configuration)
-    {
-        configuration.add("barney", "BARNEY");
-        configuration.add("betty", "BETTY");
-    }
+public class BarneyModule {
+  @Match(
+      {"UnorderedNames", "Fred", "PrivateFredAlias"})
+  @Order("after:Beta")
+  public Object decorateGamma(Object delegate, DecoratorList list) {
+    list.add("gamma");
+
+    return null;
+  }
+
+  public Sizer buildSizer(final Map<Class, Sizer> configuration, StrategyBuilder builder) {
+
+    return builder.build(Sizer.class, configuration);
+  }
+
+  public void contributeSizer(MappedConfiguration<Class, Sizer> configuration) {
+    Sizer listSizer = new Sizer() {
+      public int size(Object object) {
+        List list = (List) object;
+
+        return list.size();
+      }
+    };
+
+    Sizer mapSizer = new Sizer() {
+      public int size(Object object) {
+        Map map = (Map) object;
+
+        return map.size();
+      }
+    };
+
+
+    configuration.add(List.class, listSizer);
+    configuration.add(Map.class, mapSizer);
+  }
+
+  @Contribute(Sizer.class)
+  public void moreSizerContributions(MappedConfiguration<Class, Sizer> configuration) {
+    Sizer defaultSizer = new Sizer() {
+      @Override
+      public int size(Object object) {
+        return 1;
+      }
+    };
+
+    Sizer nullSizer = new Sizer() {
+      @Override
+      public int size(Object object) {
+        return 0;
+      }
+    };
+
+    configuration.add(Object.class, defaultSizer);
+    configuration.add(void.class, nullSizer);
+
+  }
+
+  /**
+   * Put DecoratorList in module barney, where so it won't accidentally be decorated (which recusively builds the
+   * service, and is caught as a failure).
+   */
+  public DecoratorList buildDecoratorList() {
+    return new DecoratorList() {
+      private List<String> names = CollectionFactory.newList();
+
+      public void add(String name) {
+        names.add(name);
+      }
+
+      public List<String> getNames() {
+        return names;
+      }
+    };
+  }
+
+  public void contributeUnorderedNames(Configuration<String> configuration) {
+    configuration.add("Gamma");
+  }
+
+  public void contributeStringLookup(MappedConfiguration<String, String> configuration) {
+    configuration.add("barney", "BARNEY");
+    configuration.add("betty", "BETTY");
+  }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cf890643/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/CountingGreeterImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/CountingGreeterImpl.java b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/CountingGreeterImpl.java
index a647560..1df3097 100644
--- a/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/CountingGreeterImpl.java
+++ b/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/CountingGreeterImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2010 The Apache Software Foundation
+// Copyright 2007, 2010, 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.
@@ -18,7 +18,7 @@ public class CountingGreeterImpl implements Greeter
 {
     public CountingGreeterImpl()
     {
-        IntegrationTest.countingGreeterInstantiationCount++;
+        IntegrationTestFixture.countingGreeterInstantiationCount++;
     }
 
     public String getGreeting()