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

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

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ServiceLookupSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ServiceLookupSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ServiceLookupSpec.groovy
new file mode 100644
index 0000000..c976a73
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ServiceLookupSpec.groovy
@@ -0,0 +1,118 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.Builtin
+import org.apache.tapestry5.ioc.services.TypeCoercer
+
+import java.sql.PreparedStatement
+
+import org.apache.tapestry5.ioc.*
+
+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/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ServiceProxySpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ServiceProxySpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ServiceProxySpec.groovy
new file mode 100644
index 0000000..91a05be
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ServiceProxySpec.groovy
@@ -0,0 +1,98 @@
+package ioc.specs
+
+import 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/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/StackSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/StackSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/StackSpec.groovy
new file mode 100644
index 0000000..29116c9
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/StackSpec.groovy
@@ -0,0 +1,113 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.util.Stack
+import spock.lang.Specification
+
+class StackSpec extends Specification {
+
+  def stack = new Stack()
+
+  def "peek in empty stack is failure"() {
+
+    when:
+
+    stack.peek()
+
+    then:
+
+    IllegalStateException e = thrown()
+
+    e.message == "Stack is empty."
+  }
+
+  def "pop in empty stack is failure"() {
+
+    when:
+
+    stack.pop()
+
+    then:
+
+    IllegalStateException e = thrown()
+
+    e.message == "Stack is empty."
+  }
+
+  def "simple stack operations"() {
+
+    def fred = "fred"
+    def barney = "barney"
+
+    expect:
+
+    stack.empty
+
+    when:
+
+    stack.push fred
+
+    then:
+
+    stack.peek().is(fred)
+    !stack.empty
+
+    when:
+
+    stack.push barney
+
+    then:
+
+    stack.peek().is(barney)
+
+    stack.toString() == "Stack[barney, fred]"
+
+    stack.depth == 2
+
+    stack.snapshot.equals([fred, barney])
+
+    when:
+
+    def popped = stack.pop()
+
+    then:
+
+    popped.is barney
+    stack.peek().is(fred)
+    !stack.empty
+
+    when:
+
+    popped = stack.pop()
+
+    then:
+
+    popped.is fred
+    stack.empty
+  }
+
+  def "force the expansion of the inner data"() {
+
+    def limit = 1000
+
+    when:
+
+    limit.times { stack.push it }
+
+    then:
+
+    limit.downto(1) { stack.pop() == it - 1}
+  }
+
+  def "clear the stack"() {
+
+    10.times { stack.push it }
+
+    when:
+
+    stack.clear()
+
+    then:
+
+    stack.empty
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/StrategyBuilderImplSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/StrategyBuilderImplSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/StrategyBuilderImplSpec.groovy
new file mode 100644
index 0000000..9f24309
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/StrategyBuilderImplSpec.groovy
@@ -0,0 +1,69 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.services.StrategyBuilder
+import spock.lang.Shared
+
+
+interface KindOf {
+
+  String kindOf(Object value);
+}
+
+class KindOfImpl implements KindOf {
+
+  private final String value;
+
+  KindOfImpl(String value) { this.value = value; }
+
+  @Override
+  String kindOf(Object value) {
+    return this.value;
+  }
+
+}
+
+class StrategyBuilderImplSpec extends AbstractSharedRegistrySpecification {
+
+  @Shared
+  KindOf service
+
+
+  def setup() {
+
+    StrategyBuilder builder = getService StrategyBuilder
+
+    service = builder.build KindOf, [
+        (Map): new KindOfImpl("MAP"),
+        (List): new KindOfImpl("LIST")
+    ]
+  }
+
+  def "generated class implements a useful toString()"() {
+
+    expect:
+
+    service.toString() == "<Strategy for ioc.specs.KindOf>"
+  }
+
+  def "ensure implementation inputs map to interface definitions"() {
+
+    expect:
+
+    service.kindOf(Collections.EMPTY_MAP) == "MAP"
+    service.kindOf(Collections.EMPTY_LIST) == "LIST"
+  }
+
+  def "mapping null with no void mapping is a failure"() {
+
+    when:
+
+    service.kindOf null
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == "No adapter from type void to type ioc.specs.KindOf is available."
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/StrategyRegistrySpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/StrategyRegistrySpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/StrategyRegistrySpec.groovy
new file mode 100644
index 0000000..821cbab
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/StrategyRegistrySpec.groovy
@@ -0,0 +1,148 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.util.StrategyRegistry
+import org.apache.tapestry5.ioc.util.UnknownValueException
+import spock.lang.Specification
+
+class StrategyRegistrySpec extends Specification {
+
+  def "check exception when an adaptor is not found"() {
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    StrategyRegistry reg = StrategyRegistry.newInstance(Runnable, [
+        (List): r1,
+        (Map): r2
+    ])
+
+    when:
+
+    reg.get(Set)
+
+    then:
+
+    UnknownValueException e = thrown()
+
+    e.message == "No adapter from type java.util.Set to type java.lang.Runnable is available."
+    e.availableValues.toString() == "AvailableValues[registered types: interface java.util.List, interface java.util.Map]"
+
+  }
+
+  def "access to types registered"() {
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    when:
+
+    StrategyRegistry sr = StrategyRegistry.newInstance(Runnable, [
+        (List): r1,
+        (Map): r2
+    ])
+
+    then:
+
+    sr.types.size == 2
+    sr.types.containsAll(List, Map)
+  }
+
+  def "locate an adapter based on interface inheritance"() {
+
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    when:
+
+    StrategyRegistry sr = StrategyRegistry.newInstance(Runnable, [
+        (List): r1,
+        (Map): r2
+    ])
+
+    def arrayListAdapter = sr.get(ArrayList)
+
+    then:
+
+    arrayListAdapter.is r1
+
+    when:
+
+    def adapter2 = sr.get(ArrayList)
+
+    then:
+
+    adapter2.is r1
+
+    when:
+
+    sr.clearCache()
+
+    def adapter3 = sr.get(ArrayList)
+
+    then:
+
+    adapter3.is r1
+  }
+
+  def "the registration map passed to the constructor is copied"() {
+
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    def registrations = [
+        (List): r1,
+        (Map): r2
+    ]
+
+    when:
+
+    StrategyRegistry sr = StrategyRegistry.newInstance(Runnable, registrations)
+
+    registrations.clear()
+
+    then:
+
+    sr.get(ArrayList).is(r1)
+  }
+
+  def "adapter found from an instance"() {
+
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    when:
+
+    StrategyRegistry sr = StrategyRegistry.newInstance(Runnable, [
+        (List): r1,
+        (Map): r2
+    ])
+
+    then:
+
+    sr.getByInstance([]).is(r1)
+    sr.getByInstance([:]).is(r2)
+
+    when:
+
+    sr.clearCache()
+
+    then:
+
+    sr.getByInstance([]).is(r1)
+  }
+
+  def "null instances matches against void.class"() {
+
+    Runnable r1 = Mock()
+    Runnable r2 = Mock()
+
+    when:
+
+    def sr = StrategyRegistry.newInstance(Runnable, [
+        (void): r1,
+        (Map): r2])
+
+
+    then:
+
+    sr.getByInstance(null).is(r1)
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/StringToEnumCoercionSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/StringToEnumCoercionSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/StringToEnumCoercionSpec.groovy
new file mode 100644
index 0000000..ce8a473
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/StringToEnumCoercionSpec.groovy
@@ -0,0 +1,53 @@
+package ioc.specs
+
+import org.apache.tapestry5.util.Stooge
+import org.apache.tapestry5.util.StringToEnumCoercion
+import spock.lang.Specification
+
+class StringToEnumCoercionSpec extends Specification {
+
+  def "searches are case-insensitive"() {
+    def coercion = new StringToEnumCoercion(Stooge)
+
+    expect:
+
+    coercion.coerce("moe").is(Stooge.MOE)
+    coercion.coerce("MOE").is(Stooge.MOE)
+    coercion.coerce("CURLY_Joe").is(Stooge.CURLY_JOE)
+  }
+
+  def "blank input returns null"() {
+    def coercion = new StringToEnumCoercion(Stooge)
+
+    expect:
+
+    coercion.coerce("") == null
+    coercion.coerce("\t\n") == null
+  }
+
+  def "enum value can be found by an added alias"() {
+    def coercion = new StringToEnumCoercion(Stooge)
+
+    coercion.addAlias("shemp", Stooge.CURLY_JOE)
+
+    expect:
+
+    coercion.coerce("curly_joe").is(Stooge.CURLY_JOE)
+    coercion.coerce("shemp").is(Stooge.CURLY_JOE)
+    coercion.coerce("Shemp").is(Stooge.CURLY_JOE)
+  }
+
+  def "a failed search by name throws an exception"() {
+    def coercion = new StringToEnumCoercion(Stooge)
+
+    when:
+
+    coercion.coerce("shemp")
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == /Input 'shemp' does not identify a value from enumerated type ${Stooge.name}./
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/SystemEnvSymbolProviderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/SystemEnvSymbolProviderSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/SystemEnvSymbolProviderSpec.groovy
new file mode 100644
index 0000000..d9f72d0
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/SystemEnvSymbolProviderSpec.groovy
@@ -0,0 +1,21 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.services.SystemEnvSymbolProvider
+import org.apache.tapestry5.ioc.services.SymbolProvider
+import spock.lang.Specification
+
+class SystemEnvSymbolProviderSpec extends Specification {
+
+  SymbolProvider provider = new SystemEnvSymbolProvider()
+
+  def "key exists"() {
+    expect:
+    provider.valueForSymbol("env.home") == System.getenv("HOME")
+  }
+
+  def "key missing"() {
+    expect: provider.valueForSymbol("env.does-not-exist") == null
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/TimeIntervalSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/TimeIntervalSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/TimeIntervalSpec.groovy
new file mode 100644
index 0000000..992f692
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/TimeIntervalSpec.groovy
@@ -0,0 +1,72 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.util.TimeInterval
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class TimeIntervalSpec extends Specification {
+
+  @Unroll
+  def "constructor usage '#input' parses to #milliseconds ms and '#units'"() {
+    when:
+
+    TimeInterval ti = new TimeInterval(input)
+
+    then:
+
+    ti.milliseconds() == milliseconds
+    ti.toString() == "TimeInterval[$units]"
+
+    ti.toDescription() == description
+
+    where:
+
+    input    | milliseconds            | units          | description
+
+    "30 s"   | 30000                   | "30000 ms"     | "30s"
+    "1h 30m" | 90 * 60 * 1000          | "5400000 ms"   | "1h 30m"
+    "2d"     | 2 * 24 * 60 * 60 * 1000 | "172800000 ms" | "2d"
+    "23ms"   | 23                      | "23 ms"        | "23ms"
+    "62s"    | 62 * 1000               | "62000 ms"     | "1m 2s"
+  }
+
+  def "invalid units"() {
+
+    when:
+
+    TimeInterval.parseMilliseconds "30s 500mz"
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message == "Unknown time interval unit 'mz' (in '30s 500mz').  Defined units: d, h, m, ms, s, y."
+  }
+
+  def "unrecognized input"() {
+
+    when:
+
+    TimeInterval.parseMilliseconds "30s z 500ms"
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Unexpected string 'z'"
+  }
+
+  def "unrecognized input at end"() {
+
+    when:
+
+    TimeInterval.parseMilliseconds "30s  500ms xyz"
+
+    then:
+
+    RuntimeException e = thrown()
+
+    e.message.contains "Unexpected string 'xyz'"
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ToString.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ToString.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ToString.groovy
new file mode 100644
index 0000000..b432ee0
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ToString.groovy
@@ -0,0 +1,7 @@
+package ioc.specs
+
+/** An interface that includes toString() */
+interface ToString {
+
+  String toString();
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/URLChangeTrackerSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/URLChangeTrackerSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/URLChangeTrackerSpec.groovy
new file mode 100644
index 0000000..a5a8c56
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/URLChangeTrackerSpec.groovy
@@ -0,0 +1,187 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.internal.services.ClasspathURLConverterImpl
+import org.apache.tapestry5.ioc.internal.util.URLChangeTracker
+import org.apache.tapestry5.ioc.services.ClasspathURLConverter
+import spock.lang.Shared
+import spock.lang.Specification
+
+class URLChangeTrackerSpec extends Specification {
+
+  @Shared
+  ClasspathURLConverter converter = new ClasspathURLConverterImpl()
+
+  def tracker = new URLChangeTracker(converter)
+
+  def "new instance does not contain changes"() {
+    expect:
+
+    !tracker.containsChanges()
+  }
+
+  def "adding a null URL returns 0"() {
+
+    expect:
+    tracker.add(null) == 0l
+  }
+
+  def touch(file) {
+
+    def initial = file.lastModified()
+    def index = 0
+
+    while (true) {
+
+      file.lastModified = System.currentTimeMillis()
+
+      if (file.lastModified() != initial) { return }
+
+      Thread.sleep(50 * 2 ^ index++)
+    }
+  }
+
+  def "add a file, touch it, and ensure that the change is noticed"() {
+    def f = newFile()
+
+    when:
+
+    tracker.add(f.toURL())
+
+    then:
+
+    // one for the file, one for its directory
+
+    tracker.trackedFileCount() == 2
+    !tracker.containsChanges()
+
+    when:
+
+    touch(f)
+
+    then:
+
+    tracker.containsChanges()
+  }
+
+  def File newFile() {
+    File.createTempFile("changetracker0", ".tmp")
+  }
+
+  def "creating a new file in an existing tracker folder is a change"() {
+
+    def first = newFile()
+    def dir = first.getParentFile()
+
+    when:
+
+    tracker.add(first.toURL())
+
+    then:
+
+    !tracker.containsChanges()
+
+    when:
+
+    def initial = dir.lastModified()
+    def index = 0
+
+    while (true) {
+      newFile()
+
+      if (dir.lastModified() != initial) { break; }
+      Thread.sleep(50 * 2 ^ index++)
+    }
+
+    then:
+
+    tracker.containsChanges()
+  }
+
+  def "non-file URLs are ignored"() {
+
+    when:
+
+    tracker.add(new URL("http://google.com"))
+
+    then:
+
+    tracker.trackedFileCount() == 0
+  }
+
+  def "caching of URLs and timestamps"() {
+
+    def file = newFile()
+    def url = file.toURL()
+
+    def initial = tracker.add(url)
+
+    when:
+
+    touch(file)
+
+    then:
+
+    tracker.add(url) == initial
+
+    tracker.containsChanges()
+
+    when:
+
+    tracker.clear()
+
+    then:
+
+    tracker.add(url) != initial
+  }
+
+  def "deleting a file shows as changes"() {
+    def file = newFile()
+    def url = file.toURL()
+
+    when:
+
+    def initial = tracker.add(url)
+
+    then:
+
+    initial > 0
+    !tracker.containsChanges()
+
+    when:
+
+    file.delete()
+
+    then:
+
+    tracker.containsChanges()
+  }
+
+  def "can track changes at a 1-second granularity (rather than millisecond)"() {
+    tracker = new URLChangeTracker(converter, true, true)
+
+    def file = newFile()
+
+    when:
+
+    long initial = tracker.add(file.toURL())
+
+    then:
+
+    initial % 1000 == 0
+
+    when:
+
+    Thread.sleep 1500
+
+    touch(file)
+
+    then:
+
+    tracker.containsChanges()
+
+    def updated = tracker.add(file.toURL())
+
+    updated % 1000 == 0
+    updated != initial
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingConfigurationWrapperSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingConfigurationWrapperSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingConfigurationWrapperSpec.groovy
new file mode 100644
index 0000000..c76cb3c
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingConfigurationWrapperSpec.groovy
@@ -0,0 +1,80 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.Configuration
+import org.apache.tapestry5.ioc.ObjectLocator
+import org.apache.tapestry5.ioc.internal.TypeCoercerProxy
+import org.apache.tapestry5.ioc.internal.ValidatingConfigurationWrapper
+import spock.lang.Specification
+
+class ValidatingConfigurationWrapperSpec extends Specification {
+
+  TypeCoercerProxy tc = Mock()
+  ObjectLocator locator = Mock()
+
+  def collection = []
+
+  def "valid contribution"() {
+    Runnable value = Mock()
+
+    Configuration config = new ValidatingConfigurationWrapper(Runnable, null, tc, collection, "Baz")
+
+    when:
+
+    config.add(value)
+
+    then:
+
+    tc.coerce(value, Runnable) >> value
+
+    collection == [value]
+  }
+
+  def "contributed value may be coerced"() {
+    Runnable value = Mock()
+    Runnable coerced = Mock()
+
+    Configuration config = new ValidatingConfigurationWrapper(Runnable, null, tc, collection, "Baz")
+
+    when:
+
+    config.add(value)
+
+    then:
+
+    tc.coerce(value, Runnable) >> coerced
+
+    collection == [coerced]
+  }
+
+  def "an instance of a class may be contributed"() {
+    HashMap contributed = new HashMap()
+    Map coerced = Mock()
+
+    Configuration config = new ValidatingConfigurationWrapper(Map, locator, tc, collection, "Baz")
+
+    when:
+
+    config.addInstance(HashMap)
+
+    then:
+
+    locator.autobuild(HashMap) >> contributed
+    tc.coerce(contributed, Map) >> coerced
+
+    collection == [coerced]
+  }
+
+  def "null may not be contributed"() {
+    Configuration config = new ValidatingConfigurationWrapper(Runnable, null, tc, collection, "Baz")
+
+    when:
+
+    config.add(null)
+
+    then:
+
+    NullPointerException e = thrown()
+
+    e.message == "Service contribution (to service 'Baz') was null."
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingMappedConfigurationWrapperSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingMappedConfigurationWrapperSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingMappedConfigurationWrapperSpec.groovy
new file mode 100644
index 0000000..60241a7
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingMappedConfigurationWrapperSpec.groovy
@@ -0,0 +1,154 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.MappedConfiguration
+import org.apache.tapestry5.ioc.ObjectLocator
+import org.apache.tapestry5.ioc.def.ContributionDef
+import org.apache.tapestry5.ioc.internal.ContributionDefImpl
+import org.apache.tapestry5.ioc.internal.TypeCoercerProxy
+import org.apache.tapestry5.ioc.internal.ValidatingMappedConfigurationWrapper
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory
+
+import java.sql.SQLException
+
+class ValidatingMappedConfigurationWrapperSpec extends AbstractSharedRegistrySpecification {
+
+  static String SERVICE_ID = "Baz"
+
+  ObjectLocator locator = Mock()
+  TypeCoercerProxy tc = Mock()
+  Map keyToContribution = [:]
+  Map map = [:]
+
+  def "contribute a property key and value"() {
+    ContributionDef cd = Mock()
+    def keyToContribution = [:]
+    ObjectLocator locator = Mock()
+    def map = [:]
+    TypeCoercerProxy tc = Mock()
+    Runnable value = Mock()
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, cd, Class, keyToContribution)
+
+    when:
+
+    config.add(Integer, value)
+
+    then:
+
+    tc.coerce(value, Runnable) >> value
+
+    map[Integer].is(value)
+    keyToContribution[Integer].is(cd)
+  }
+
+  def "an added value may be coerced to the correct type"() {
+
+    ContributionDef cd = Mock()
+    def value = "coerce-me"
+    Runnable coerced = Mock()
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, cd, Class, keyToContribution)
+
+    when:
+
+    config.add(Integer, value)
+
+    then:
+
+    tc.coerce(value, Runnable) >> coerced
+
+    map[Integer].is(coerced)
+    keyToContribution[Integer].is(cd)
+  }
+
+  def ContributionDef newContributionDef(methodName) {
+
+    def proxyFactory = getService PlasticProxyFactory
+
+    return new ContributionDefImpl(SERVICE_ID, findMethod(methodName), false, proxyFactory, null, null);
+  }
+
+  def findMethod(name) {
+    return this.class.methods.find() { it.name == name }
+  }
+
+
+  public void contributionPlaceholder1() {
+
+  }
+
+  public void contributionPlaceholder2() {
+
+  }
+
+  def "may not contribute a duplicate key"() {
+    ContributionDef def1 = newContributionDef "contributionPlaceholder1"
+    ContributionDef def2 = newContributionDef "contributionPlaceholder2"
+
+    keyToContribution[Integer] = def1
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, def2, Class, keyToContribution)
+
+    when:
+
+    config.add(Integer, "does-not-matter")
+
+    then:
+
+    IllegalArgumentException e = thrown()
+
+    e.message.contains "Service contribution (to service 'Baz') conflicts with existing contribution"
+
+    keyToContribution[Integer].is(def1)
+    map.isEmpty()
+  }
+
+  def "the contributed key may not be null"() {
+    ContributionDef cd = newContributionDef "contributionPlaceholder1"
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, cd, Class, keyToContribution)
+
+    when:
+
+    config.add(null, "does-not-matter")
+
+    then:
+
+    NullPointerException e = thrown()
+
+    e.message == "Key for service contribution (to service '$SERVICE_ID') was null."
+  }
+
+  def "adding a key of the wrong type is an exception"() {
+    ContributionDef cd = newContributionDef "contributionPlaceholder1"
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, cd, Class, keyToContribution)
+
+    when:
+
+    config.add("java.util.List", "does-not-matter")
+
+    then:
+
+    IllegalArgumentException e = thrown()
+
+    e.message == "Key for service contribution (to service 'Baz') was an instance of java.lang.String, but the expected key type was java.lang.Class."
+  }
+
+  def "contributing a null value is an exception"() {
+    ContributionDef cd = newContributionDef "contributionPlaceholder1"
+
+    MappedConfiguration config = new ValidatingMappedConfigurationWrapper(Runnable, locator, tc, map, null, SERVICE_ID, cd, Class, keyToContribution)
+
+    when:
+
+    config.add(SQLException, null)
+
+    then:
+
+    NullPointerException e = thrown()
+
+    e.message == "Service contribution (to service 'Baz') was null."
+    map.isEmpty()
+  }
+}
\ 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/ValidatingOrderedConfigurationWrapperSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingOrderedConfigurationWrapperSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingOrderedConfigurationWrapperSpec.groovy
new file mode 100644
index 0000000..3255c49
--- /dev/null
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/ValidatingOrderedConfigurationWrapperSpec.groovy
@@ -0,0 +1,82 @@
+package ioc.specs
+
+import org.apache.tapestry5.ioc.ObjectLocator
+import org.apache.tapestry5.ioc.OrderedConfiguration
+import org.apache.tapestry5.ioc.internal.TypeCoercerProxy
+import org.apache.tapestry5.ioc.internal.ValidatingOrderedConfigurationWrapper
+import org.apache.tapestry5.ioc.internal.util.Orderer
+import org.slf4j.Logger
+import spock.lang.Specification
+
+class ValidatingOrderedConfigurationWrapperSpec extends Specification {
+
+  def "contribution of a coerceable instance"() {
+    Runnable contribution = Mock()
+    Runnable coerced = Mock()
+    Runnable pre = Mock()
+    Runnable post = Mock()
+    Logger logger = Mock()
+    TypeCoercerProxy tc = Mock()
+
+    def orderer = new Orderer(logger)
+
+    orderer.add "pre", pre
+    orderer.add "post", post
+
+    OrderedConfiguration config = new ValidatingOrderedConfigurationWrapper(Runnable, null, tc, orderer, null, null)
+
+    when:
+
+    config.add("id", contribution, "after:pre", "before:post")
+
+    then:
+
+    1 * tc.coerce(contribution, Runnable) >> coerced
+
+    orderer.ordered == [pre, coerced, post]
+  }
+
+  def "contribution of a valid type"() {
+    Map instance = new HashMap()
+    Map pre = Mock()
+    Map post = Mock()
+    ObjectLocator locator = Mock()
+    TypeCoercerProxy tc = Mock()
+    Logger logger = Mock()
+
+    def orderer = new Orderer(logger)
+
+    orderer.add "pre", pre
+    orderer.add "post", post
+
+    OrderedConfiguration config = new ValidatingOrderedConfigurationWrapper(Map, locator, tc, orderer, null, null)
+
+    when:
+
+    config.addInstance("id", HashMap, "after:pre", "before:post")
+
+    then:
+
+    1 * locator.autobuild(HashMap) >> instance
+    1 * tc.coerce(instance, Map) >> instance
+
+    orderer.ordered == [pre, instance, post]
+  }
+
+  def "null objected passed through"() {
+    Logger logger = Mock()
+
+    Orderer orderer = new Orderer(logger)
+    OrderedConfiguration config = new ValidatingOrderedConfigurationWrapper(Runnable, null, null, orderer, null, null)
+
+    when:
+
+    config.add("id", null)
+
+    then:
+
+    orderer.ordered.empty
+
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractRegistrySpecification.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractRegistrySpecification.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractRegistrySpecification.groovy
deleted file mode 100644
index 25dc3f9..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractRegistrySpecification.groovy
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.apache.tapestry5.ioc
-
-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/org/apache/tapestry5/ioc/AbstractSharedRegistrySpecification.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractSharedRegistrySpecification.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractSharedRegistrySpecification.groovy
deleted file mode 100644
index 7115ae9..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AbstractSharedRegistrySpecification.groovy
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.apache.tapestry5.ioc
-
-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/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
deleted file mode 100644
index d4abc93..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AdvisorsSpec.groovy
+++ /dev/null
@@ -1,83 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index d5aa6a5..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/AutobuildSpec.groovy
+++ /dev/null
@@ -1,171 +0,0 @@
-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/a1bef869/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/BaseLocatableSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/BaseLocatableSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/BaseLocatableSpec.groovy
deleted file mode 100644
index 9344b13..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/BaseLocatableSpec.groovy
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.apache.tapestry5.ioc
-
-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/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
deleted file mode 100644
index d0db942..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ConfigurationsSpec.groovy
+++ /dev/null
@@ -1,316 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index 8dfa727..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/DecoratorsSpec.groovy
+++ /dev/null
@@ -1,109 +0,0 @@
-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/a1bef869/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/EagerLoadSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/EagerLoadSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/EagerLoadSpec.groovy
deleted file mode 100644
index 7175751..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/EagerLoadSpec.groovy
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.apache.tapestry5.ioc
-
-class EagerLoadSpec extends AbstractRegistrySpecification {
-
-  def "proxied service does eager load"() {
-    expect:
-
-    EagerProxyReloadModule.eagerLoadServiceDidLoad == false
-
-    when:
-
-    buildRegistry EagerProxyReloadModule
-
-    performRegistryStartup()
-
-    then:
-
-    EagerProxyReloadModule.eagerLoadServiceDidLoad == true
-  }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/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
deleted file mode 100644
index 24952ac..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/InjectionSpec.groovy
+++ /dev/null
@@ -1,123 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index d466772..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ManifestProcessingSpec.groovy
+++ /dev/null
@@ -1,36 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index 4b5f40b..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ModuleInstantiationSpec.groovy
+++ /dev/null
@@ -1,53 +0,0 @@
-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/a1bef869/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/OrderedConstraintBuilderSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/OrderedConstraintBuilderSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/OrderedConstraintBuilderSpec.groovy
deleted file mode 100644
index 18c0cbe..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/OrderedConstraintBuilderSpec.groovy
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.apache.tapestry5.ioc
-
-import spock.lang.Specification
-
-class OrderedConstraintBuilderSpec extends Specification {
-
-  /** Any unrecognized methods are evaluated against {@link OrderConstraintBuilder}. */
-  def methodMissing(String name, args) {
-    OrderConstraintBuilder."$name"(* args)
-  }
-
-  def "constraint ordering"() {
-    expect:
-
-    constraint.build() == values
-
-    where:
-
-    constraint || values
-
-    after("A") || ["after:A"]
-    afterAll() || ["after:*"]
-    before("B") || ["before:B"]
-    beforeAll() || ["before:*"]
-
-    before("A").after("B") || ["before:A", "after:B"]
-  }
-
-
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/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
deleted file mode 100644
index 9533862..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/PerThreadScopeSpec.groovy
+++ /dev/null
@@ -1,88 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index fd3acad..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryBuilderSpec.groovy
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.apache.tapestry5.ioc
-
-import org.apache.tapestry5.ioc.def.ModuleDef
-import org.apache.tapestry5.ioc.internal.DefaultModuleDefImpl
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import spock.lang.Specification
-
-class RegistryBuilderSpec extends Specification {
-
-  def "@SubModule annotation is honored"() {
-    when:
-
-    Registry r = new RegistryBuilder().add(MasterModule).build()
-
-    def service = r.getService("UnorderedNames", NameListHolder)
-
-    then:
-
-    service.names == ["Beta", "Gamma", "UnorderedNames"]
-
-    cleanup:
-
-    r.shutdown()
-  }
-
-  def "adding modules by name, in comma seperated list, as from a manifest"() {
-    when:
-
-    RegistryBuilder builder = new RegistryBuilder()
-
-    IOCUtilities.addModulesInList builder,
-        "${FredModule.class.name}, ${BarneyModule.class.name}, ${RegistryBuilderTestModule.class.name}"
-
-    Registry registry = builder.build()
-
-    Square service = registry.getService(Square)
-
-    then:
-
-    service.square(4) == 16
-
-    service.toString() == "<Proxy for Square(${Square.class.name})>"
-
-    cleanup:
-
-    registry.shutdown()
-  }
-
-  def "exercise RegistryBuilder.buildAndStartupRegistry()"() {
-    when:
-
-    Registry r = RegistryBuilder.buildAndStartupRegistry(MasterModule);
-
-    NameListHolder service = r.getService("UnorderedNames", NameListHolder);
-
-    then:
-
-    service.names == ["Beta", "Gamma", "UnorderedNames"]
-
-    cleanup:
-
-    r.shutdown();
-  }
-
-  def "use explicit ModuleDef with buildAndStartupRegistry()"() {
-    when:
-
-    Logger logger = LoggerFactory.getLogger(getClass());
-
-    ModuleDef module = new DefaultModuleDefImpl(ServiceBuilderModule,
-        logger, null);
-
-    Registry r = RegistryBuilder.buildAndStartupRegistry(module, MasterModule);
-
-    NameListHolder nameListHolder = r.getService("UnorderedNames", NameListHolder);
-
-    then:
-
-    nameListHolder.names == ["Beta", "Gamma", "UnorderedNames"]
-
-    when:
-
-    Greeter greeter = r.getService("Greeter", Greeter)
-
-    then:
-
-    greeter.greeting == "Greetings from service Greeter."
-
-    cleanup:
-
-    r.shutdown();
-
-  }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/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
deleted file mode 100644
index 70da305..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistryConstructionAndRuntimeErrorsSpec.groovy
+++ /dev/null
@@ -1,117 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index d8432cc..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/RegistrySpec.groovy
+++ /dev/null
@@ -1,53 +0,0 @@
-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/a1bef869/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ReloadSpec.groovy
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ReloadSpec.groovy b/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ReloadSpec.groovy
deleted file mode 100644
index 6838d81..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ReloadSpec.groovy
+++ /dev/null
@@ -1,401 +0,0 @@
-package org.apache.tapestry5.ioc
-
-import org.apache.tapestry5.internal.plastic.PlasticInternalUtils
-import org.apache.tapestry5.internal.plastic.asm.ClassWriter
-import org.apache.tapestry5.services.UpdateListenerHub
-import spock.lang.AutoCleanup
-import spock.lang.Specification
-import com.example.*
-
-import static org.apache.tapestry5.internal.plastic.asm.Opcodes.*
-
-class ReloadSpec extends Specification {
-
-  private static final String PACKAGE = "com.example";
-
-  private static final String CLASS = PACKAGE + ".ReloadableServiceImpl";
-
-  private static final String BASE_CLASS = PACKAGE + ".BaseReloadableServiceImpl";
-
-
-  @AutoCleanup("shutdown")
-  Registry registry
-
-  @AutoCleanup("deleteDir")
-  File classesDir
-
-  ClassLoader classLoader
-
-  File classFile;
-
-  def createRegistry() {
-    registry = new RegistryBuilder(classLoader).add(ReloadModule).build()
-  }
-
-  /** Any unrecognized methods are evaluated against the registry. */
-  def methodMissing(String name, args) {
-    registry."$name"(* args)
-  }
-
-
-  def setup() {
-    def uid = UUID.randomUUID().toString()
-
-    classesDir = new File(System.getProperty("java.io.tmpdir"), uid)
-
-    def classesURL = new URL("file:" + classesDir.getCanonicalPath() + "/")
-
-    classLoader = new URLClassLoader([classesURL] as URL[],
-        Thread.currentThread().contextClassLoader)
-
-    classFile = new File(classesDir, PlasticInternalUtils.toClassPath(CLASS))
-  }
-
-  def createImplementationClass(String status) {
-    createImplementationClass CLASS, status
-  }
-
-  def createImplementationClass(String className, String status) {
-
-    String internalName = PlasticInternalUtils.toInternalName className
-
-    createClassWriter(internalName, "java/lang/Object", ACC_PUBLIC).with {
-
-      // Add default constructor
-
-      visitMethod(ACC_PUBLIC, "<init>", "()V", null, null).with {
-        visitCode()
-        visitVarInsn ALOAD, 0
-        visitMethodInsn INVOKESPECIAL, "java/lang/Object", "<init>", "()V"
-        visitInsn RETURN
-        visitMaxs 1, 1
-        visitEnd()
-      }
-
-
-      visitMethod(ACC_PUBLIC, "getStatus", "()Ljava/lang/String;", null, null).with {
-        visitCode()
-        visitLdcInsn status
-        visitInsn ARETURN
-        visitMaxs 1, 1
-        visitEnd()
-      }
-
-      visitEnd()
-
-      writeBytecode it, internalName
-    }
-
-  }
-
-  def createClassWriter(String internalName, String baseClassInternalName, int classModifiers) {
-    ClassWriter cw = new ClassWriter(0);
-
-    cw.visit V1_5, classModifiers, internalName, null,
-        baseClassInternalName, [
-            PlasticInternalUtils.toInternalName(ReloadableService.name)
-        ] as String[]
-
-
-    return cw
-  }
-
-
-  def writeBytecode(ClassWriter cw, String internalName) {
-    byte[] bytecode = cw.toByteArray();
-
-    writeBytecode(bytecode, pathForInternalName(internalName))
-  }
-
-  def writeBytecode(byte[] bytecode, String path) {
-    File file = new File(path)
-
-    file.parentFile.mkdirs()
-
-    file.withOutputStream { it.write bytecode }
-  }
-
-
-  def pathForInternalName(String internalName) {
-    return String.format("%s/%s.class",
-        classesDir.getAbsolutePath(),
-        internalName)
-  }
-
-  def update() {
-    getService(UpdateListenerHub).fireCheckForUpdates()
-  }
-
-  def "reload a service implementation"() {
-
-    when:
-
-    createImplementationClass "initial"
-
-    createRegistry()
-
-    ReloadableService reloadable = getService(ReloadableService);
-
-    update()
-
-    then:
-
-    reloadable.status == "initial"
-
-    when:
-
-    update()
-
-    touch classFile
-
-    createImplementationClass "updated"
-
-    then:
-
-    // Changes do not take effect until after update check
-
-    reloadable.status == "initial"
-
-    when:
-
-    update()
-
-    then:
-
-    reloadable.status == "updated"
-  }
-
-  def "reload a base class"() {
-
-    setup:
-
-    def baseClassInternalName = PlasticInternalUtils.toInternalName BASE_CLASS
-    def internalName = PlasticInternalUtils.toInternalName CLASS
-
-    createImplementationClass BASE_CLASS, "initial from base"
-
-    createClassWriter(internalName, baseClassInternalName, ACC_PUBLIC).with {
-
-      visitMethod(ACC_PUBLIC, "<init>", "()V", null, null).with {
-        visitCode()
-        visitVarInsn ALOAD, 0
-        visitMethodInsn INVOKESPECIAL, baseClassInternalName, "<init>", "()V"
-        visitInsn RETURN
-        visitMaxs 1, 1
-        visitEnd()
-      }
-
-      visitEnd()
-
-      writeBytecode it, internalName
-    }
-
-    createRegistry()
-
-    when:
-
-    ReloadableService reloadable = getService(ReloadableService)
-
-    update()
-
-    then:
-
-    reloadable.status == "initial from base"
-
-    when:
-
-    touch(new File(pathForInternalName(baseClassInternalName)))
-
-    createImplementationClass BASE_CLASS, "updated from base"
-
-    update()
-
-    then:
-
-    reloadable.status == "updated from base"
-  }
-
-  def "deleting an implementation class results in a runtime exception when reloading"() {
-
-    when:
-
-    createImplementationClass "before delete"
-
-    createRegistry()
-
-    ReloadableService reloadable = getService ReloadableService
-
-    then:
-
-    reloadable.status == "before delete"
-
-    assert classFile.exists()
-
-    when:
-
-    classFile.delete()
-
-    update()
-
-    reloadable.getStatus()
-
-    then:
-
-    RuntimeException e = thrown()
-
-    e.message.contains "Unable to reload class $CLASS"
-  }
-
-
-  def "reload a proxy object"() {
-    when:
-
-    createImplementationClass "initial proxy"
-
-    createRegistry()
-
-    def clazz = classLoader.loadClass CLASS
-
-    ReloadableService reloadable = proxy(ReloadableService, clazz)
-
-    then:
-
-    reloadable.status == "initial proxy"
-
-    when:
-
-    touch classFile
-
-    createImplementationClass "updated proxy"
-
-    update()
-
-    then:
-
-    reloadable.status == "updated proxy"
-
-    when:
-
-    touch classFile
-
-    createImplementationClass "re-updated proxy"
-
-    update()
-
-    then:
-
-    reloadable.status == "re-updated proxy"
-  }
-
-  def "check exception message for invalid service implementation (lacking a public constructor)"() {
-
-    when:
-
-    createImplementationClass "initial"
-
-    createRegistry()
-
-    ReloadableService reloadable = getService ReloadableService
-
-    touch classFile
-
-    createInvalidImplementationClass()
-
-    update()
-
-    reloadable.getStatus()
-
-    then:
-
-    Exception e = thrown()
-
-    e.message == "Service implementation class com.example.ReloadableServiceImpl does not have a suitable public constructor."
-  }
-
-  def "ensure ReloadAware services are notified when services are reloaded"() {
-
-    when:
-
-    registry = new RegistryBuilder().add(ReloadAwareModule).build()
-
-    then:
-
-    ReloadAwareModule.counterInstantiations == 0
-    ReloadAwareModule.counterReloads == 0
-
-    when:
-
-    Counter counter = proxy(Counter, CounterImpl)
-
-    then:
-
-    ReloadAwareModule.counterInstantiations == 0
-
-    expect:
-
-    counter.increment() == 1
-    counter.increment() == 2
-
-    ReloadAwareModule.counterInstantiations == 1
-
-    when:
-
-    def classURL = CounterImpl.getResource("CounterImpl.class")
-    def classFile = new File(classURL.toURI())
-
-    touch classFile
-
-    update()
-
-    // Check that the internal state has reset
-
-    assert counter.increment() == 1
-
-    then:
-
-    ReloadAwareModule.counterInstantiations == 2
-    ReloadAwareModule.counterReloads == 1
-  }
-
-  def createInvalidImplementationClass() {
-    def internalName = PlasticInternalUtils.toInternalName CLASS
-
-    createClassWriter(internalName, "java/lang/Object", ACC_PUBLIC).with {
-
-      visitMethod(ACC_PROTECTED, "<init>", "()V", null, null).with {
-        visitVarInsn ALOAD, 0
-        visitMethodInsn INVOKESPECIAL, "java/lang/Object", "<init>", "()V"
-        visitInsn RETURN
-        visitMaxs 1, 1
-        visitEnd()
-      }
-
-      visitEnd()
-
-      writeBytecode it, internalName
-    }
-  }
-
-
-  def touch(File f) {
-    long startModified = f.lastModified();
-
-    int index = 0;
-
-    while (true) {
-      f.setLastModified System.currentTimeMillis()
-
-      long newModified = f.lastModified()
-
-      if (newModified != startModified) {
-        return;
-      }
-
-      // Sleep an ever increasing amount, to ensure that the filesystem
-      // catches the change to the file. The Ubuntu CI Server appears
-      // to need longer waits.
-
-      Thread.sleep 50 * (2 ^ index++)
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/a1bef869/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
deleted file mode 100644
index 98a4914..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceActivityScoreboardSpec.groovy
+++ /dev/null
@@ -1,74 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index 30448fc..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceBinderSpec.groovy
+++ /dev/null
@@ -1,35 +0,0 @@
-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/a1bef869/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
deleted file mode 100644
index 5a9828a..0000000
--- a/tapestry-ioc/src/test/groovy/org/apache/tapestry5/ioc/ServiceLookupSpec.groovy
+++ /dev/null
@@ -1,116 +0,0 @@
-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!"
-  }
-
-}