You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/09/18 03:51:41 UTC

[isis] branch v2 updated: ISIS-1976: rename module 'unittestsupport-test' -> 'detached-tests'

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new 4efa188  ISIS-1976: rename module 'unittestsupport-test' -> 'detached-tests'
4efa188 is described below

commit 4efa188814c12211a2f196b517a88914276c1513
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Sep 18 05:51:32 2018 +0200

    ISIS-1976: rename module 'unittestsupport-test' -> 'detached-tests'
    
    naming better reflects the fact, that the module collects tests that
    needed to be detached from their originating module in order to break
    cyclic module dependencies
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-1976
---
 core/detached-tests/.gitignore                     |   1 +
 core/detached-tests/pom.xml                        | 164 ++++++++++
 .../ServiceInjectorTestUsingCodegenPlugin.java     | 153 ++++++++++
 .../ServiceInstantiatorTestUsingCodegenPlugin.java | 219 ++++++++++++++
 .../BidirectionalRelationshipContractTestAll.java  |  36 +++
 .../unittestsupport/bidir/ChildDomainObject.java   |  76 +++++
 .../bidir/InstantiatorForChildDomainObject.java    |  36 +++
 .../unittestsupport/bidir/ParentDomainObject.java  |  65 ++++
 .../unittestsupport/bidir/PeerDomainObject.java    | 103 +++++++
 .../bidir/PeerDomainObjectForTesting.java          |  23 ++
 .../comparable/CategorizedDomainObject.java        |  70 +++++
 ...mainObjectComparableContractTest_compareTo.java |  49 +++
 .../files/FilesTest_deleteFiles.java               |  83 ++++++
 ...ectServiceMethodMustBeFinalContractTestAll.java |  28 ++
 .../unittestsupport/inject/SomeDomainObject.java   |  26 ++
 .../core/unittestsupport/inject/SomeService.java   |  21 ++
 .../CollaboratingUsingConstructorInjection.java    |  37 +++
 .../CollaboratingUsingSetterInjection.java         |  40 +++
 .../unittestsupport/jmocking/Collaborator.java     |  24 ++
 .../ImposteriserTestUsingCodegenPlugin.java        | 329 +++++++++++++++++++++
 ...IsisActionsTest_returnNewTransientInstance.java |  93 ++++++
 .../jmocking/JMockActionsTest_returnArgument.java  |  68 +++++
 .../jmocking/JMockActionsTest_returnEach.java      |  70 +++++
 .../jmocking/JUnitRuleMockery2Test.java            |  46 +++
 ..._autoWiring_constructorInjection_happyCase.java |  57 ++++
 ...y2Test_autoWiring_sadCase_noClassUnderTest.java |  45 +++
 ...est_autoWiring_sadCase_twoClassesUnderTest.java |  50 ++++
 ...2Test_autoWiring_setterInjection_happyCase.java |  58 ++++
 ...RuleMockery2Test_mockAnnotatedWithAllowing.java |  60 ++++
 ...RuleMockery2Test_mockAnnotatedWithChecking.java |  62 ++++
 ...RuleMockery2Test_mockAnnotatedWithIgnoring.java |  59 ++++
 ...kery2Test_mockAnnotatedWithNever_happyCase.java |  54 ++++
 ...ockery2Test_mockAnnotatedWithNever_sadCase.java |  59 ++++
 ...ockery2Test_mockAnnotatedWithOne_happyCase.java |  55 ++++
 ...eMockery2Test_mockAnnotatedWithOne_sadCase.java |  56 ++++
 .../sortedsets/SomeDomainObject.java               |  35 +++
 .../sortedsets/SortedSetsContractTestAll.java      |  28 ++
 ...lueTypeContractTestAbstract_BigIntegerTest.java |  37 +++
 .../ValueTypeContractTestAbstract_ColorTest.java   |  38 +++
 .../ValueTypeContractTestAbstract_StringTest.java  |  36 +++
 .../proxy/ProxyCreatorTestUsingCodegenPlugin.java  |  97 ++++++
 .../src/test/resources/signed.jar                  | Bin
 core/detached-tests/xml/objects/CUS.xml            |   0
 core/detached-tests/xml/objects/CUS/1.txt          |   0
 core/detached-tests/xml/objects/CUS/1.xml          |   0
 core/detached-tests/xml/objects/CUS/2.xml          |   0
 core/pom.xml                                       |   2 +-
 47 files changed, 2747 insertions(+), 1 deletion(-)

diff --git a/core/detached-tests/.gitignore b/core/detached-tests/.gitignore
new file mode 100644
index 0000000..a48e45b
--- /dev/null
+++ b/core/detached-tests/.gitignore
@@ -0,0 +1 @@
+/target-ide
diff --git a/core/detached-tests/pom.xml b/core/detached-tests/pom.xml
new file mode 100644
index 0000000..2ee0477
--- /dev/null
+++ b/core/detached-tests/pom.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+	license agreements. See the NOTICE file distributed with this work for additional 
+	information regarding copyright ownership. The ASF licenses this file to 
+	you under the Apache License, Version 2.0 (the "License"); you may not use 
+	this file except in compliance with the License. You may obtain a copy of 
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+	by applicable law or agreed to in writing, software distributed under the 
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. See the License for the specific 
+	language governing permissions and limitations under the License. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.core</groupId>
+		<artifactId>isis</artifactId>
+		<version>${revision}</version>
+	</parent>
+
+	<artifactId>isis-core-detached-tests</artifactId>
+	<name>Apache Isis Detached Tests (tests only)</name>
+
+	<properties>
+		<jar-plugin.automaticModuleName>org.apache.isis.core.detached-tests</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/isis/core/detached-tests</git-plugin.propertiesDir>
+	</properties>
+
+	<description>
+		To break cyclic dependencies some tests needed to be detached from their originating module and moved here.
+		And some tests require presence of core-plugins on the class-path, these also moved here. 
+	</description>
+
+	<dependencies>
+	
+		<!-- core plugin loading -->
+		<dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-commons</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-unittestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
+        
+        <dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-core-wrapper</artifactId>
+			<scope>test</scope>
+		</dependency>
+        
+        <!-- core plugins required to run this module's own tests -->
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-discovery-reflections</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-plugins-codegen-bytebuddy</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+
+		<!-- JUPITER -->
+		<dependency>
+			<!-- JUnit API for writing tests and extensions -->
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-api</artifactId>
+		</dependency>
+		<dependency>
+			<!-- implementation of the JUnit Jupiter test engine -->
+			<groupId>org.junit.jupiter</groupId>
+			<artifactId>junit-jupiter-engine</artifactId>
+		</dependency>
+		<dependency>
+			<!-- support for unit tests that use JUnit 4 or JUnit 3 -->
+			<groupId>org.junit.vintage</groupId>
+			<artifactId>junit-vintage-engine</artifactId>
+		</dependency>
+		<dependency>
+			<!-- JUnit 4 compile dependency in unittestsupport's src/main/java folder, non-transitive -->
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>compile</scope>
+			<optional>true</optional>
+		</dependency>
+
+		<dependency>
+			<groupId>org.picocontainer</groupId>
+			<artifactId>picocontainer</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jmock</groupId>
+			<artifactId>jmock</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>org.jmock</groupId>
+			<artifactId>jmock-junit4</artifactId>
+		</dependency>
+		
+		<!-- JDO API (non transient, provided by plugin) -->
+		<dependency>
+			<groupId>javax.jdo</groupId>
+			<artifactId>jdo-api</artifactId>
+			<version>${jdo-api.version}</version>
+			<!-- provided by plugins -->
+			<scope>provided</scope>
+		</dependency>
+
+	</dependencies>
+
+	<profiles>
+		<profile>
+			<id>flatten</id>
+			<activation>
+				<property>
+					<name>!skip.flatten</name>
+				</property>
+			</activation>
+			<build>
+				<plugins>
+					<plugin>
+						<groupId>org.codehaus.mojo</groupId>
+						<artifactId>flatten-maven-plugin</artifactId>
+						<version>1.0.0</version>
+						<executions>
+							<execution>
+								<id>flatten</id>
+								<phase>process-resources</phase>
+								<goals>
+									<goal>flatten</goal>
+								</goals>
+								<configuration>
+									<flattenMode>defaults</flattenMode>
+									<updatePomFile>true</updatePomFile>
+									<pomElements>
+										<name>resolve</name>
+										<description>resolve</description>
+										<dependencies>resolve</dependencies>
+									</pomElements>
+								</configuration>
+							</execution>
+							<execution>
+								<id>flatten.clean</id>
+								<phase>clean</phase>
+								<goals>
+									<goal>clean</goal>
+								</goals>
+							</execution>
+						</executions>
+					</plugin>
+				</plugins>
+			</build>
+		</profile>
+	</profiles>
+
+</project>
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInjectorTestUsingCodegenPlugin.java b/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInjectorTestUsingCodegenPlugin.java
new file mode 100644
index 0000000..c3cccd2
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInjectorTestUsingCodegenPlugin.java
@@ -0,0 +1,153 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.runtime.services;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.enterprise.context.RequestScoped;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.commons.internal.collections._Lists;
+import org.apache.isis.core.commons.config.IsisConfiguration;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+
+public class ServiceInjectorTestUsingCodegenPlugin {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(JUnitRuleMockery2.Mode.INTERFACES_AND_CLASSES);
+
+    private ServiceInstantiator serviceInstantiator;
+    private ServicesInjector serviceInjector;
+
+    @Before
+    public void setUp() throws Exception {
+
+        final IsisConfiguration configuration = new IsisConfigurationDefault();
+        
+        serviceInstantiator = new ServiceInstantiator();
+        serviceInstantiator.setConfiguration(configuration);
+        
+        final Object[] services = {
+                serviceInstantiator.createInstance(SingletonCalculator.class),
+                serviceInstantiator.createInstance(AccumulatingCalculator.class)
+                };
+        
+        serviceInjector = new ServicesInjector(_Lists.of(services), configuration);
+    }
+
+    @Test
+    public void singleton() {
+        SingletonCalculator calculator = serviceInjector.lookupService(SingletonCalculator.class);
+        assertThat(calculator.add(3), is(3));
+        calculator = serviceInjector.lookupService(SingletonCalculator.class);
+        assertThat(calculator.add(4), is(7));
+    }
+
+    @Test
+    public void requestScoped_instantiate() {
+        final AccumulatingCalculator calculator = serviceInjector.lookupService(AccumulatingCalculator.class);
+        assertThat(calculator instanceof RequestScopedService, is(true));
+    }
+
+    @Test
+    public void requestScoped_justOneThread() {
+        final AccumulatingCalculator calculator = serviceInjector.lookupService(AccumulatingCalculator.class);
+        
+        try {
+            ((RequestScopedService)calculator).__isis_startRequest(serviceInjector);
+            assertThat(calculator.add(3), is(3));
+            assertThat(calculator.add(4), is(7));
+            assertThat(calculator.getTotal(), is(7));
+        } finally {
+            ((RequestScopedService)calculator).__isis_endRequest();
+        }
+    }
+
+    @Test
+    public void requestScoped_multipleThreads() throws InterruptedException, ExecutionException {
+        
+        final AccumulatingCalculator calculator = serviceInjector.lookupService(AccumulatingCalculator.class);
+        final ExecutorService executor = Executors.newFixedThreadPool(10);
+        
+        // setup 32 tasks
+        final List<Callable<Integer>> tasks = IntStream.range(0, 32)
+        .<Callable<Integer>>mapToObj(index->()->{
+            
+            // within each task setup a new calculator instance that adds the numbers from 1 .. 100 = 5050
+            ((RequestScopedService)calculator).__isis_startRequest(serviceInjector);
+            for(int i=1; i<=100; i++) {
+                calculator.add(i);    
+            }
+            try {
+                return calculator.getTotal();
+            } finally {
+                ((RequestScopedService)calculator).__isis_endRequest();
+            }
+        })
+        .collect(Collectors.toList());
+        
+        
+        final List<Future<Integer>> results = executor.invokeAll(tasks);
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+
+        // we expect that each of the 32 calculators have calculated the sum correctly
+        for(Future<Integer> future: results) {
+            assertThat(future.get(), is(5050));
+        }
+    }
+
+    public static class SingletonCalculator {
+        private int total;
+        public int add(int x) {
+            total += x;
+            return getTotal();
+        }
+        public int getTotal() {
+            return total;
+        }
+    }
+
+    @RequestScoped
+    public static class AccumulatingCalculator {
+        private int total;
+        public int add(int x) {
+            total += x;
+            return getTotal();
+        }
+        public int getTotal() {
+            return total;
+        }
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTestUsingCodegenPlugin.java b/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTestUsingCodegenPlugin.java
new file mode 100644
index 0000000..c8e354c
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/runtime/services/ServiceInstantiatorTestUsingCodegenPlugin.java
@@ -0,0 +1,219 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.runtime.services;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.enterprise.context.RequestScoped;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Lists;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.metamodel.services.ServicesInjector;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+
+public class ServiceInstantiatorTestUsingCodegenPlugin {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(JUnitRuleMockery2.Mode.INTERFACES_AND_CLASSES);
+
+    private ServiceInstantiator serviceInstantiator;
+
+    @JUnitRuleMockery2.Ignoring
+    @Mock
+    private ServicesInjector mockServiceInjector;
+
+    @Before
+    public void setUp() throws Exception {
+
+        serviceInstantiator = new ServiceInstantiator();
+        serviceInstantiator.setConfiguration(new IsisConfigurationDefault());
+    }
+
+    @Test
+    public void singleton() {
+        SingletonCalculator calculator = serviceInstantiator.createInstance(SingletonCalculator.class);
+        assertThat(calculator.add(3,4), is(7));
+    }
+
+    @Test
+    public void requestScoped_instantiate() {
+        AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+        assertThat(calculator instanceof RequestScopedService, is(true));
+    }
+
+    @Test
+    public void requestScoped_justOneThread() {
+        AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+        try {
+            ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
+            assertThat(calculator.add(3), is(3));
+            assertThat(calculator.add(4), is(7));
+            assertThat(calculator.getTotal(), is(7));
+        } finally {
+            ((RequestScopedService)calculator).__isis_endRequest();
+        }
+    }
+
+    @Test
+    public void requestScoped_multipleThreads() throws InterruptedException, BrokenBarrierException {
+
+        final AccumulatingCalculator calculator = serviceInstantiator.createInstance(AccumulatingCalculator.class);
+
+        // will ask each thread's calculator to increment 10 times
+        final int[] steps = new int[]{10};
+
+        // each thread will post its totals here
+        final int[] totals = new int[]{0,0,0};
+
+        // after each step, all threads wait.  The +1 is for this thread (the co-ordinator)
+        final CyclicBarrier barrier =
+                new CyclicBarrier(totals.length+1, new Runnable() {
+                    @Override
+                    public void run() {
+                        // all threads waiting; decrement number of steps
+                        steps[0]--;
+                    }
+                });
+
+        // start off all threads
+        for(int i=0; i<totals.length; i++) {
+            final int j=i;
+            new Thread() {
+                @Override
+                public void run() {
+                    try {
+                        ((RequestScopedService)calculator).__isis_startRequest(mockServiceInjector);
+                        // keep incrementing, till no more steps
+                        while(steps[0]>0) {
+                            try {
+                                calculator.add((j+1));
+                                totals[j] = calculator.getTotal();
+                                barrier.await();
+                            } catch (InterruptedException | BrokenBarrierException e) {
+                                throw new RuntimeException(e);
+                            }
+                        }
+                    } finally {
+                        ((RequestScopedService)calculator).__isis_endRequest();
+                    }
+                };
+            }.start();
+        }
+
+        // this thread is the co-ordinator; move onto next step when all are waiting
+        while(steps[0]>0) {
+            barrier.await();
+        }
+
+        assertThat(totals[0], is(10));
+        assertThat(totals[1], is(20));
+        assertThat(totals[2], is(30));
+    }
+
+    @Test
+    public void requestScoped_childThreads() throws InterruptedException  {
+
+        final Consumer consumer = serviceInstantiator.createInstance(Consumer.class);
+
+        final List<Integer> allTheNumbers = Collections.synchronizedList(Lists.<Integer>newArrayList());
+
+        final int n = 100;
+        for (int i = 0; i < n; i++) {
+            allTheNumbers.add(i);
+        }
+
+        final int nThreads = 8;
+        final ExecutorService execService = Executors.newFixedThreadPool(nThreads);
+
+        // initialize the request scoped calculator on current thread ('main')
+        ((RequestScopedService)consumer).__isis_startRequest(mockServiceInjector);
+
+        for (int i = 0; i < n; i++) {
+            final int j=i;
+
+            execService.submit(new Runnable() {
+                @Override public void run() {
+                    try {
+
+                        // access the request scoped calculator on a child thread of 'main'
+                        consumer.consume(allTheNumbers, j);
+
+                    } catch (Exception e) {
+                        System.err.println(e.getMessage());
+                    }
+                }
+            });
+
+        }
+
+        execService.shutdown();
+
+        execService.awaitTermination(10, TimeUnit.SECONDS);
+
+        ((RequestScopedService)consumer).__isis_endRequest();
+
+        assertEquals(0, FluentIterable.from(allTheNumbers).filter(Predicates.<Integer>notNull()).size());
+    }
+
+    public static class SingletonCalculator {
+        public int add(int x, int y) {
+            return x+y;
+        }
+    }
+
+    @RequestScoped
+    public static class AccumulatingCalculator {
+        private int total;
+        public int add(int x) {
+            total += x;
+            return getTotal();
+        }
+        public int getTotal() {
+            return total;
+        }
+    }
+
+    @RequestScoped
+    public static class Consumer {
+        public void consume(final List<Integer> queue, final int slot) {
+            synchronized (queue) {
+                final Integer integer = queue.get(slot);
+                if(integer != null) {
+                    queue.set(slot, null);
+                }
+            }
+        }
+    }
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java
new file mode 100644
index 0000000..9b1f50c
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/BidirectionalRelationshipContractTestAll.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import com.google.common.collect.ImmutableMap;
+
+public class BidirectionalRelationshipContractTestAll extends BidirectionalRelationshipContractTestAbstract {
+
+    public BidirectionalRelationshipContractTestAll() {
+        super("org.apache.isis.core.unittestsupport.bidir",
+                ImmutableMap.<Class<?>,Instantiator>of(
+                        // no instantiator need be registered for ParentDomainObject.class;
+                        // will default to using new InstantiatorSimple(AgreementForTesting.class),
+                        ChildDomainObject.class, new InstantiatorForChildDomainObject(),
+                        PeerDomainObject.class, new InstantiatorSimple(PeerDomainObjectForTesting.class)
+                        ));
+        withLoggingTo(System.out);
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java
new file mode 100644
index 0000000..59c9e9e
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ChildDomainObject.java
@@ -0,0 +1,76 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+
+@PersistenceCapable
+public class ChildDomainObject implements Comparable<ChildDomainObject> {
+
+    // {{ Index (property)
+    private int index;
+
+    public int getIndex() {
+        return index;
+    }
+
+    public void setIndex(final int index) {
+        this.index = index;
+    }
+    // }}
+
+
+    // {{ Parent (property)
+    private ParentDomainObject parent;
+
+    public ParentDomainObject getParent() {
+        return parent;
+    }
+
+    public void setParent(final ParentDomainObject parent) {
+        this.parent = parent;
+    }
+    public void modifyParent(final ParentDomainObject parent) {
+        ParentDomainObject currentParent = getParent();
+        // check for no-op
+        if (parent == null || parent.equals(currentParent)) {
+            return;
+        }
+        // delegate to parent to associate
+        parent.addToChildren(this);
+    }
+
+    public void clearParent() {
+        ParentDomainObject currentParent = getParent();
+        // check for no-op
+        if (currentParent == null) {
+            return;
+        }
+        // delegate to parent to dissociate
+        currentParent.removeFromChildren(this);
+    }
+    // }}
+
+
+
+    @Override
+    public int compareTo(ChildDomainObject other) {
+        return this.getIndex() - other.getIndex();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java
new file mode 100644
index 0000000..2e096b6
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/InstantiatorForChildDomainObject.java
@@ -0,0 +1,36 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.util.SortedSet;
+
+/**
+ * To ensure that different ({@link Comparable}) {@link ChildDomainObject}s
+ * are not equivalent when placed into the {@link ParentDomainObject#getChildren() children} (a {@link SortedSet}.)
+ */
+public class InstantiatorForChildDomainObject implements Instantiator {
+
+    private int i;
+
+    @Override
+    public Object instantiate() {
+        final ChildDomainObject cdo = new ChildDomainObject();
+        cdo.setIndex(++i);
+        return cdo;
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java
new file mode 100644
index 0000000..f9d94e2
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/ParentDomainObject.java
@@ -0,0 +1,65 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+@PersistenceCapable
+public class ParentDomainObject {
+
+    // {{ Children (Collection)
+    @Persistent(mappedBy="parent")
+    private SortedSet<ChildDomainObject> children = new TreeSet<ChildDomainObject>();
+
+    public SortedSet<ChildDomainObject> getChildren() {
+        return children;
+    }
+
+    public void setChildren(final SortedSet<ChildDomainObject> children) {
+        this.children = children;
+    }
+
+    public void addToChildren(final ChildDomainObject child) {
+        // check for no-op
+        if (child == null || getChildren().contains(child)) {
+            return;
+        }
+        // dissociate arg from its current parent (if any).
+        child.clearParent();
+        // associate arg
+        child.setParent(this);
+        getChildren().add(child);
+    }
+
+    public void removeFromChildren(final ChildDomainObject child) {
+        // check for no-op
+        if (child == null || !getChildren().contains(child)) {
+            return;
+        }
+        // dissociate arg
+        child.setParent(null);
+        getChildren().remove(child);
+    }
+
+    // }}
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java
new file mode 100644
index 0000000..14b5881
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObject.java
@@ -0,0 +1,103 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.PersistenceCapable;
+import javax.jdo.annotations.Persistent;
+
+/**
+ * Not instantiable; the {@link PeerDomainObjectForTesting} must be used in the bidir contract testing instead.
+ */
+@PersistenceCapable
+public abstract class PeerDomainObject {
+
+    // {{ Next (property)
+    @Persistent(mappedBy="previous")
+    private PeerDomainObject next;
+
+    public PeerDomainObject getNext() {
+        return next;
+    }
+
+    public void setNext(final PeerDomainObject next) {
+        this.next = next;
+    }
+    public void modifyNext(final PeerDomainObject next) {
+        PeerDomainObject currentNext = getNext();
+        // check for no-op
+        if (next == null || next.equals(currentNext)) {
+            return;
+        }
+        // dissociate existing
+        clearNext();
+        // associate new
+        next.setPrevious(this);
+        setNext(next);
+    }
+
+    public void clearNext() {
+        PeerDomainObject currentNext = getNext();
+        // check for no-op
+        if (currentNext == null) {
+            return;
+        }
+        // dissociate existing
+        currentNext.setPrevious(null);
+        setNext(null);
+    }
+    // }}
+
+
+
+    // {{ Previous (property)
+    private PeerDomainObject previous;
+
+    public PeerDomainObject getPrevious() {
+        return previous;
+    }
+
+    public void setPrevious(final PeerDomainObject previous) {
+        this.previous = previous;
+    }
+
+    public void modifyPrevious(final PeerDomainObject previous) {
+        PeerDomainObject currentPrevious = getPrevious();
+        // check for no-op
+        if (previous == null || previous.equals(currentPrevious)) {
+            return;
+        }
+        // delegate to parent(s) to (re-)associate
+        if (currentPrevious != null) {
+            currentPrevious.clearNext();
+        }
+        previous.modifyNext(this);
+    }
+
+    public void clearPrevious() {
+        PeerDomainObject currentPrevious = getPrevious();
+        // check for no-op
+        if (currentPrevious == null) {
+            return;
+        }
+        // delegate to parent to dissociate
+        currentPrevious.clearNext();
+    }
+    // }}
+
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java
new file mode 100644
index 0000000..ed4fc70
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/bidir/PeerDomainObjectForTesting.java
@@ -0,0 +1,23 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.bidir;
+
+import javax.jdo.annotations.Persistent;
+
+public class PeerDomainObjectForTesting extends PeerDomainObject {
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObject.java
new file mode 100644
index 0000000..de361dd
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObject.java
@@ -0,0 +1,70 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.comparable;
+
+import com.google.common.collect.Ordering;
+
+
+public class CategorizedDomainObject implements Comparable<CategorizedDomainObject> {
+
+    private Integer category;
+
+    public Integer getCategory() {
+        return category;
+    }
+
+    public void setCategory(final Integer category) {
+        this.category = category;
+    }
+
+
+
+    private Integer subcategory;
+
+    public Integer getSubcategory() {
+        return subcategory;
+    }
+
+    public void setSubcategory(final Integer subcategory) {
+        this.subcategory = subcategory;
+    }
+
+
+
+    @Override
+    public int compareTo(CategorizedDomainObject other) {
+        return ORDER_BY_CATEGORY.compound(ORDER_BY_SUBCATEGORY).compare(this, other);
+    }
+
+    private static Ordering<CategorizedDomainObject> ORDER_BY_CATEGORY = new Ordering<CategorizedDomainObject>() {
+        @Override
+        public int compare(CategorizedDomainObject left, CategorizedDomainObject right) {
+            return Ordering.natural().nullsFirst().compare(left.getCategory(), right.getCategory());
+        }
+    };
+
+    private static Ordering<CategorizedDomainObject> ORDER_BY_SUBCATEGORY = new Ordering<CategorizedDomainObject>() {
+        @Override
+        public int compare(CategorizedDomainObject left, CategorizedDomainObject right) {
+            return Ordering.natural().nullsFirst().compare(left.getSubcategory(), right.getSubcategory());
+        }
+
+    };
+
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObjectComparableContractTest_compareTo.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObjectComparableContractTest_compareTo.java
new file mode 100644
index 0000000..ad84432
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/comparable/CategorizedDomainObjectComparableContractTest_compareTo.java
@@ -0,0 +1,49 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.comparable;
+
+import java.util.List;
+
+public class CategorizedDomainObjectComparableContractTest_compareTo extends ComparableContractTest_compareTo<CategorizedDomainObject> {
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected List<List<CategorizedDomainObject>> orderedTuples() {
+        return listOf(
+                listOf(
+                        newObject(null, null),
+                        newObject(1, null),
+                        newObject(1, null),
+                        newObject(2, null)
+                        ),
+                listOf(
+                        newObject(1, null),
+                        newObject(1, 1),
+                        newObject(1, 1),
+                        newObject(1, 2)
+                        )
+                );
+    }
+
+    private CategorizedDomainObject newObject(Integer category, Integer subcategory) {
+        final CategorizedDomainObject obj = new CategorizedDomainObject();
+        obj.setCategory(category);
+        obj.setSubcategory(subcategory);
+        return obj;
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/files/FilesTest_deleteFiles.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/files/FilesTest_deleteFiles.java
new file mode 100644
index 0000000..9fe39f5
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/files/FilesTest_deleteFiles.java
@@ -0,0 +1,83 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.files;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.files.Files.Deleter;
+import org.apache.isis.core.unittestsupport.files.Files.Recursion;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class FilesTest_deleteFiles {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Deleter deleter;
+
+    @Test
+    public void test() throws IOException {
+        final File cusIdxFile = new File("xml/objects/CUS.xml");
+        final File cus1File = new File("xml/objects/CUS/1.xml");
+        final File cus2File = new File("xml/objects/CUS/2.xml");
+        context.checking(new Expectations() {
+            {
+                oneOf(deleter).deleteFile(with(equalsFile(cusIdxFile)));
+                oneOf(deleter).deleteFile(with(equalsFile(cus1File)));
+                oneOf(deleter).deleteFile(with(equalsFile(cus2File)));
+            }
+        });
+
+        Files.deleteFiles(new File("xml/objects"), Files.filterFileNameExtension(".xml"), Recursion.DO_RECURSE, deleter);
+    }
+
+
+    private static Matcher<File> equalsFile(final File file) throws IOException {
+        final String canonicalPath = file.getCanonicalPath();
+        return new TypeSafeMatcher<File>() {
+
+            @Override
+            public void describeTo(Description arg0) {
+                arg0.appendText("file '" + canonicalPath + "'");
+            }
+
+            @Override
+            public boolean matchesSafely(File arg0) {
+                try {
+                    return arg0.getCanonicalPath().equals(canonicalPath);
+                } catch (IOException e) {
+                    return false;
+                }
+            }
+        };
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/InjectServiceMethodMustBeFinalContractTestAll.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/InjectServiceMethodMustBeFinalContractTestAll.java
new file mode 100644
index 0000000..8fcb405
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/InjectServiceMethodMustBeFinalContractTestAll.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.inject;
+
+public class InjectServiceMethodMustBeFinalContractTestAll extends InjectServiceMethodMustBeFinalContractTestAbstract {
+
+    public InjectServiceMethodMustBeFinalContractTestAll() {
+        super("org.apache.isis.core.unittestsupport.inject");
+        withLoggingTo(System.out);
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeDomainObject.java
new file mode 100644
index 0000000..da62b6f
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeDomainObject.java
@@ -0,0 +1,26 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.inject;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+@PersistenceCapable
+public class SomeDomainObject {
+
+    public final void injectFoo(SomeService someService) {}
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeService.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeService.java
new file mode 100644
index 0000000..e3f2e86
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/inject/SomeService.java
@@ -0,0 +1,21 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.inject;
+
+public interface SomeService {
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingConstructorInjection.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingConstructorInjection.java
new file mode 100644
index 0000000..d065771
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingConstructorInjection.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+public class CollaboratingUsingConstructorInjection {
+    final Collaborator collaborator;
+
+    public CollaboratingUsingConstructorInjection(final Collaborator collaborator) {
+        this.collaborator = collaborator;
+    }
+
+    public void collaborateWithCollaborator() {
+        collaborator.doOtherStuff();
+    }
+
+    public void dontCollaborateWithCollaborator() {
+
+    }
+
+}
\ No newline at end of file
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingSetterInjection.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingSetterInjection.java
new file mode 100644
index 0000000..496b4d6
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/CollaboratingUsingSetterInjection.java
@@ -0,0 +1,40 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+public class CollaboratingUsingSetterInjection {
+    Collaborator collaborator;
+
+    public CollaboratingUsingSetterInjection() {
+    }
+
+    public void setCollaborator(Collaborator collaborator) {
+        this.collaborator = collaborator;
+    }
+
+    public void collaborateWithCollaborator() {
+        collaborator.doOtherStuff();
+    }
+
+    public void dontCollaborateWithCollaborator() {
+
+    }
+
+}
\ No newline at end of file
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/Collaborator.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/Collaborator.java
new file mode 100644
index 0000000..06b626b
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/Collaborator.java
@@ -0,0 +1,24 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+public interface Collaborator {
+    public void doOtherStuff();
+}
\ No newline at end of file
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java
new file mode 100644
index 0000000..51955d1
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java
@@ -0,0 +1,329 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Date;
+
+import org.apache.isis.commons.internal.context._Context;
+import org.apache.isis.core.plugins.codegen.ProxyFactoryPlugin;
+import org.jmock.api.Imposteriser;
+import org.jmock.api.Invocation;
+import org.jmock.api.Invokable;
+import org.jmock.lib.action.VoidAction;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ImposteriserTestUsingCodegenPlugin {
+
+    private Imposteriser imposteriser = Imposterisers.getDefault();
+
+    private Invokable invokable;
+    @SuppressWarnings("unused")
+    private Invocation invocation;
+
+    @Before
+    public void setUp() throws Exception {
+        invokable = new Invokable() {
+            @Override
+            public Object invoke(Invocation invocation) throws Throwable {
+                ImposteriserTestUsingCodegenPlugin.this.invocation = invocation;
+                return "result";
+            }
+        };
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        invokable = null;
+        invocation = null;
+        _Context.clear(); // removes plugins from context
+    }
+
+    // //////////////////////////////////////
+
+    @Test
+    public void canLoadCodegenPlugin() throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
+        Assert.assertNotNull(ProxyFactoryPlugin.get());
+    }
+
+    // //////////////////////////////////////
+
+    @Test
+    public void happyCaseWhenJdkInterface() {
+        assertTrue(imposteriser.canImposterise(Runnable.class));
+        final Runnable imposter = imposteriser.imposterise(invokable, Runnable.class);
+        assertNotNull(imposter);
+        imposter.run();
+    }
+
+    @Test
+    public void happyCaseWhenJdkClass() {
+        assertTrue(imposteriser.canImposterise(Date.class));
+        final Date imposter = imposteriser.imposterise(invokable, Date.class);
+        assertNotNull(imposter);
+        imposter.toString();
+    }
+
+
+    // //////////////////////////////////////
+
+    @Test
+    public void cannotImposterisePrimitiveType() {
+        assertFalse(imposteriser.canImposterise(int.class));
+    }
+
+    @Test
+    public void cannotImposteriseVoidType() {
+        assertFalse(imposteriser.canImposterise(void.class));
+    }
+
+
+    // //////////////////////////////////////
+
+
+    public static abstract class AnAbstractNestedClass {
+        public abstract String foo();
+    }
+
+    @Test
+    public void happyCaseWhenAbstractClass() {
+        assertTrue(imposteriser.canImposterise(AnAbstractNestedClass.class));
+        final AnAbstractNestedClass imposter = imposteriser.imposterise(invokable, AnAbstractNestedClass.class);
+        assertNotNull(imposter);
+        assertEquals("result", imposter.foo());
+    }
+
+
+    // //////////////////////////////////////
+
+
+
+    public static class AnInnerClass {
+        public String foo() {return "original result";}
+    }
+
+    @Test
+    public void happyCaseWhenNonFinalInstantiableClass() {
+        assertTrue(imposteriser.canImposterise(AnInnerClass.class));
+        final AnInnerClass imposter = imposteriser.imposterise(invokable, AnInnerClass.class);
+        assertNotNull(imposter);
+        assertEquals("result", imposter.foo());
+    }
+
+
+
+    // //////////////////////////////////////
+
+
+    public static final class AFinalClass {
+        public String foo() {return "original result";}
+    }
+
+    @Test
+    public void cannotImposteriseWhenFinalInstantiableClasses() {
+        assertFalse(imposteriser.canImposterise(AFinalClass.class));
+    }
+
+
+    // //////////////////////////////////////
+
+
+
+    public static class AClassWithAPrivateConstructor {
+        @SuppressWarnings("unused")
+        private AClassWithAPrivateConstructor(String someArgument) {}
+
+        public String foo() {return "original result";}
+    }
+
+    @Test
+    public void happyCaseWhenClassWithNonPublicConstructor() {
+        assertTrue(imposteriser.canImposterise(AClassWithAPrivateConstructor.class));
+        AClassWithAPrivateConstructor imposter =
+                imposteriser.imposterise(invokable, AClassWithAPrivateConstructor.class);
+
+        assertNotNull(imposter);
+        assertEquals("result", imposter.foo());
+    }
+
+
+    // //////////////////////////////////////
+
+
+
+    public static class ConcreteClassWithConstructorAndInstanceInitializer {
+        {
+            shouldNotBeCalled("instance initializer");
+        }
+
+        public ConcreteClassWithConstructorAndInstanceInitializer() {
+            shouldNotBeCalled("constructor");
+        }
+
+        public String foo() {
+            shouldNotBeCalled("method foo()");
+            return null; // never reached
+        }
+
+        private static void shouldNotBeCalled(String exceptionMessageIfCalled) {
+            throw new IllegalStateException(exceptionMessageIfCalled + " should not be called");
+        }
+    }
+
+    @Test
+    public void happyCaseWhenConcreteClassWithConstructorAndInitialisersThatShouldNotBeCalled() {
+        assertTrue(imposteriser.canImposterise(ConcreteClassWithConstructorAndInstanceInitializer.class));
+        ConcreteClassWithConstructorAndInstanceInitializer imposter =
+                imposteriser.imposterise(invokable, ConcreteClassWithConstructorAndInstanceInitializer.class);
+        assertNotNull(imposter);
+        assertEquals("result", imposter.foo());
+    }
+
+
+
+    // //////////////////////////////////////
+
+
+
+    public interface AnInterface {
+        String foo();
+    }
+
+    @Test
+    public void happyCaseWhenCustomInterface() {
+        assertTrue(imposteriser.canImposterise(AnInterface.class));
+        AnInterface imposter = imposteriser.imposterise(invokable, AnInterface.class);
+
+        assertNotNull(imposter);
+        assertEquals("result", imposter.foo());
+    }
+
+
+
+    // //////////////////////////////////////
+
+
+
+
+    @Test
+    public void happyCaseWhenClassInASignedJarFile() throws Exception {
+        File jarFile = new File("src/test/resources/signed.jar");
+
+        assertTrue(jarFile.exists());
+
+        URL jarURL = jarFile.toURI().toURL();
+        try(URLClassLoader loader = new URLClassLoader(new URL[]{jarURL})){
+            Class<?> typeInSignedJar = loader.loadClass("TypeInSignedJar");
+
+            assertTrue(imposteriser.canImposterise(typeInSignedJar));
+            Object o = imposteriser.imposterise(new VoidAction(), typeInSignedJar);
+
+            assertTrue(typeInSignedJar.isInstance(o));
+        }
+    }
+
+
+
+    // //////////////////////////////////////
+
+
+    public static class ClassWithFinalToStringMethod {
+        @Override
+        public final String toString() {
+            return "you can't override me!";
+        }
+    }
+
+    // See issue JMOCK-150
+    @Test
+    public void cannotImposteriseAClassWithAFinalToStringMethod() {
+        assertFalse(imposteriser.canImposterise(ClassWithFinalToStringMethod.class));
+
+        try {
+            imposteriser.imposterise(new VoidAction(), ClassWithFinalToStringMethod.class);
+            fail("should have thrown IllegalArgumentException");
+        }
+        catch (IllegalArgumentException expected) {
+
+        }
+    }
+
+
+
+
+
+    // //////////////////////////////////////
+
+
+    public interface EmptyInterface {}
+
+    public interface AnInterface2 {
+        String foo();
+    }
+
+
+    // See issue JMOCK-145
+    @Test
+    public void worksAroundBugInCglibWhenAskedToImposteriseObject() {
+        imposteriser.imposterise(new VoidAction(), Object.class);
+        imposteriser.imposterise(new VoidAction(), Object.class, EmptyInterface.class);
+        imposteriser.imposterise(new VoidAction(), Object.class, AnInterface2.class);
+    }
+
+    private static Object invokeMethod(Object object, Method method, Object... args) throws IllegalAccessException, InvocationTargetException {
+        method.setAccessible(true);
+        return method.invoke(object, args);
+    }
+
+
+
+    // //////////////////////////////////////
+
+
+
+    // See issue JMOCK-256 (Github #36)
+    @Test
+    public void doesntDelegateFinalizeMethod() throws Exception {
+        Invokable failIfInvokedAction = new Invokable() {
+            @Override
+            public Object invoke(Invocation invocation) throws Throwable {
+                fail("invocation should not have happened");
+                return null;
+            }
+        };
+
+        Object imposter = imposteriser.imposterise(failIfInvokedAction, Object.class);
+        invokeMethod(imposter, Object.class.getDeclaredMethod("finalize"));
+    }
+
+}
\ No newline at end of file
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/IsisActionsTest_returnNewTransientInstance.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/IsisActionsTest_returnNewTransientInstance.java
new file mode 100644
index 0000000..2bb2a5e
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/IsisActionsTest_returnNewTransientInstance.java
@@ -0,0 +1,93 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class IsisActionsTest_returnNewTransientInstance {
+
+    // we can't use the 'real' DomainObjectConainter because applib depends on this module, not vice versa
+    // but it doesn't matter; we are just testing the action (of the expectation), not the object on which
+    // we add the expectation
+    public static interface MyDomainObjectContainer {
+        <T> T newTransientInstance(Class<T> t);
+
+        void persistIfNotAlready(Object o);
+    }
+
+    public static class MyCustomer  {
+    }
+
+    @Mock
+    private MyDomainObjectContainer mockContainer;
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+
+    @Test
+    public void testIt() {
+
+        context.checking(new Expectations() {
+            {
+                allowing(mockContainer).newTransientInstance(with(anySubclassOf(Object.class)));
+                will(IsisActions.returnNewTransientInstance());
+                ignoring(mockContainer);
+            }
+        });
+
+        // is allowed (and executed)
+        MyCustomer o = mockContainer.newTransientInstance(MyCustomer.class);
+        assertThat(o, is(not(nullValue())));
+
+        // is ignored
+        mockContainer.persistIfNotAlready(o);
+    }
+
+
+    private static <X> Matcher<Class<X>> anySubclassOf(final Class<X> cls) {
+        return new TypeSafeMatcher<Class<X>>() {
+
+            @Override
+            public void describeTo(final Description arg0) {
+                arg0.appendText("is subclass of ").appendText(cls.getName());
+            }
+
+            @Override
+            public boolean matchesSafely(final Class<X> item) {
+                return cls.isAssignableFrom(item);
+            }
+        };
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnArgument.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnArgument.java
new file mode 100644
index 0000000..a67fe0f
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnArgument.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class JMockActionsTest_returnArgument {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private CollaboratorForReturnArgument collaborator;
+
+    @Test
+    public void poke() {
+        context.checking(new Expectations() {
+            {
+                oneOf(collaborator).selectOneOf(with(any(Integer.class)), with(any(Integer.class)), with(any(Integer.class)));
+                will(JMockActions.returnArgument(1)); // ie the 2nd argument, which is '20'
+            }
+        });
+        assertThat(new ClassUnderTestForReturnArgument(collaborator).addTo(4), is(24)); // adding 4 to the second argument
+    }
+
+    public interface CollaboratorForReturnArgument {
+        public int selectOneOf(int x, int y, int z);
+    }
+
+    public static class ClassUnderTestForReturnArgument {
+        private final CollaboratorForReturnArgument collaborator;
+
+        private ClassUnderTestForReturnArgument(final CollaboratorForReturnArgument collaborator) {
+            this.collaborator = collaborator;
+        }
+
+        public int addTo(int x) {
+            return x + collaborator.selectOneOf(10, 20, 30);
+        }
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnEach.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnEach.java
new file mode 100644
index 0000000..b013e68
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JMockActionsTest_returnEach.java
@@ -0,0 +1,70 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JMockActionsTest_returnEach {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private CollaboratorForReturnEach collaborator;
+
+    @Test
+    public void poke() {
+        context.checking(new Expectations() {
+            {
+                exactly(3).of(collaborator).readValue();
+                will(JMockActions.returnEach(1,2,3));
+            }
+        });
+        assertThat(new ClassUnderTestForReturnEach(collaborator).prependAndRead("foo"), is("foo 1"));
+        assertThat(new ClassUnderTestForReturnEach(collaborator).prependAndRead("bar"), is("bar 2"));
+        assertThat(new ClassUnderTestForReturnEach(collaborator).prependAndRead("baz"), is("baz 3"));
+    }
+
+    public interface CollaboratorForReturnEach {
+        public int readValue();
+    }
+
+    public static class ClassUnderTestForReturnEach {
+        private final CollaboratorForReturnEach collaborator;
+
+        private ClassUnderTestForReturnEach(final CollaboratorForReturnEach collaborator) {
+            this.collaborator = collaborator;
+        }
+
+        public String prependAndRead(String prepend) {
+            return prepend + " " + collaborator.readValue();
+        }
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test.java
new file mode 100644
index 0000000..6976961
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test.java
@@ -0,0 +1,46 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.Expectations;
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Collaborator collaborator;
+
+    @Test
+    public void poke() {
+        context.checking(new Expectations() {
+            {
+                oneOf(collaborator).doOtherStuff();
+            }
+        });
+        new CollaboratingUsingConstructorInjection(collaborator).collaborateWithCollaborator();
+    }
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_constructorInjection_happyCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_constructorInjection_happyCase.java
new file mode 100644
index 0000000..6cc10e0
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_constructorInjection_happyCase.java
@@ -0,0 +1,57 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_autoWiring_constructorInjection_happyCase {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void wiring() {
+        assertThat(collaborating.collaborator, is(not(nullValue())));
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_noClassUnderTest.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_noClassUnderTest.java
new file mode 100644
index 0000000..6c82c6c
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_noClassUnderTest.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_autoWiring_sadCase_noClassUnderTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Collaborator collaborator;
+
+    // @ClassUnderTest
+    @SuppressWarnings("unused")
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    @Test(expected=IllegalStateException.class)
+    public void cannotFindClassUnderTest() {
+        context.getClassUnderTest();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_twoClassesUnderTest.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_twoClassesUnderTest.java
new file mode 100644
index 0000000..6249a8a
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_sadCase_twoClassesUnderTest.java
@@ -0,0 +1,50 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_autoWiring_sadCase_twoClassesUnderTest {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection anotherCollaborating;
+
+    @Ignore("This isn't actually possible to test, because the test is actually thrown by the rule, which is further up the callstack than the test method")
+    @Test(expected=AssertionError.class)
+    public void cannotFindClassUnderTest() {
+        context.getClassUnderTest();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_setterInjection_happyCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_setterInjection_happyCase.java
new file mode 100644
index 0000000..0a3ef3c
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_autoWiring_setterInjection_happyCase.java
@@ -0,0 +1,58 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_autoWiring_setterInjection_happyCase {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingSetterInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void wiring() {
+        assertThat(collaborating.collaborator, is(not(nullValue())));
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithAllowing.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithAllowing.java
new file mode 100644
index 0000000..e695c3e
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithAllowing.java
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Allowing;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithAllowing {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Allowing
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.collaborateWithCollaborator();
+    }
+
+    @Test
+    public void lackOfInvocationOnCollaboratorIsIgnored() {
+        collaborating.dontCollaborateWithCollaborator();
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithChecking.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithChecking.java
new file mode 100644
index 0000000..8ec37e4
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithChecking.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Checking;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ExpectationsOn;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithChecking {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    public static class ExpectCall extends ExpectationsOn<Collaborator> {
+        public ExpectCall(Object mock) {
+            super(mock);
+        }
+        {
+            oneOf(mock()).doOtherStuff();
+        }}
+
+    @Checking(ExpectCall.class)
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.collaborateWithCollaborator();
+    }
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithIgnoring.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithIgnoring.java
new file mode 100644
index 0000000..0aee19a
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithIgnoring.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Ignoring;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithIgnoring {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Ignoring
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.collaborateWithCollaborator();
+    }
+
+    @Test
+    public void lackOfInvocationOnCollaboratorIsIgnored() {
+        collaborating.dontCollaborateWithCollaborator();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_happyCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_happyCase.java
new file mode 100644
index 0000000..197a901
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_happyCase.java
@@ -0,0 +1,54 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Never;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithNever_happyCase {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @Never
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.dontCollaborateWithCollaborator();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_sadCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_sadCase.java
new file mode 100644
index 0000000..162837e
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithNever_sadCase.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Never;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithNever_sadCase {
+
+    @Rule
+    public ExpectedException exp = ExpectedException.none();
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @SuppressWarnings("unused")
+    @Never
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        exp.expect(AssertionError.class);
+        collaborating.collaborateWithCollaborator();
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_happyCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_happyCase.java
new file mode 100644
index 0000000..00ca7ad
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_happyCase.java
@@ -0,0 +1,55 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.One;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithOne_happyCase {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @One
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Test
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.collaborateWithCollaborator();
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_sadCase.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_sadCase.java
new file mode 100644
index 0000000..072b129
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/JUnitRuleMockery2Test_mockAnnotatedWithOne_sadCase.java
@@ -0,0 +1,56 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.isis.core.unittestsupport.jmocking;
+
+import org.jmock.auto.Mock;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.ClassUnderTest;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
+import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.One;
+
+public class JUnitRuleMockery2Test_mockAnnotatedWithOne_sadCase {
+
+    @Rule
+    public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
+
+    @One
+    @Mock
+    private Collaborator collaborator;
+
+    @ClassUnderTest
+    private CollaboratingUsingConstructorInjection collaborating;
+
+    // no longer necessary :-)
+    //    @Before
+    //	public void setUp() throws Exception {
+    //    	collaborating = (CollaboratingUsingConstructorInjection) context.getClassUnderTest();
+    //	}
+
+    @Ignore("This isn't actually possible to test, because the test is actually thrown by the rule, which is further up the callstack than the test method")    @Test(expected=AssertionError.class)
+    public void invocationOnCollaboratorIsIgnored() {
+        collaborating.dontCollaborateWithCollaborator();
+    }
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SomeDomainObject.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SomeDomainObject.java
new file mode 100644
index 0000000..2529cff
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SomeDomainObject.java
@@ -0,0 +1,35 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.isis.core.unittestsupport.sortedsets;
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+@PersistenceCapable
+public class SomeDomainObject {
+
+
+    private SortedSet<SomeDomainObject> someSortedSet = new TreeSet<SomeDomainObject>();
+
+    //private Set<SomeDomainObject> someSet = new HashSet<SomeDomainObject>();
+
+    //private List<SomeDomainObject> someList = new ArrayList<SomeDomainObject>();
+
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SortedSetsContractTestAll.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SortedSetsContractTestAll.java
new file mode 100644
index 0000000..b348df1
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/sortedsets/SortedSetsContractTestAll.java
@@ -0,0 +1,28 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.sortedsets;
+
+public class SortedSetsContractTestAll extends SortedSetsContractTestAbstract {
+
+    public SortedSetsContractTestAll() {
+        super("org.apache.isis.core.unittestsupport.sortedsets");
+        withLoggingTo(System.out);
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_BigIntegerTest.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_BigIntegerTest.java
new file mode 100644
index 0000000..6904757
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_BigIntegerTest.java
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.value;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+public class ValueTypeContractTestAbstract_BigIntegerTest extends ValueTypeContractTestAbstract<BigInteger> {
+
+    @Override
+    protected List<BigInteger> getObjectsWithSameValue() {
+        return Arrays.asList(new BigInteger("1"), new BigInteger("1"));
+    }
+
+    @Override
+    protected List<BigInteger> getObjectsWithDifferentValue() {
+        return Arrays.asList(new BigInteger("2"));
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_ColorTest.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_ColorTest.java
new file mode 100644
index 0000000..f6e2bcf
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_ColorTest.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.value;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.List;
+
+
+public class ValueTypeContractTestAbstract_ColorTest extends ValueTypeContractTestAbstract<Color> {
+
+    @Override
+    protected List<Color> getObjectsWithSameValue() {
+        return Arrays.asList(new Color(127,127,127), new Color(127,127,127));
+    }
+
+    @Override
+    protected List<Color> getObjectsWithDifferentValue() {
+        return Arrays.asList(new Color(127,127,0), new Color(127,0,127), new Color(0,127,127), new Color(0,0,127), new Color(127,0,0), new Color(0,127,0));
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_StringTest.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_StringTest.java
new file mode 100644
index 0000000..d29be31
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/value/ValueTypeContractTestAbstract_StringTest.java
@@ -0,0 +1,36 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.unittestsupport.value;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ValueTypeContractTestAbstract_StringTest extends ValueTypeContractTestAbstract<String> {
+
+    @Override
+    protected List<String> getObjectsWithSameValue() {
+        return Arrays.asList(new String("1"), new String("1"));
+    }
+
+    @Override
+    protected List<String> getObjectsWithDifferentValue() {
+        return Arrays.asList(new String("1 "), new String(" 1"), new String("2"));
+    }
+
+}
diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/wrapper/proxy/ProxyCreatorTestUsingCodegenPlugin.java b/core/detached-tests/src/test/java/org/apache/isis/core/wrapper/proxy/ProxyCreatorTestUsingCodegenPlugin.java
new file mode 100644
index 0000000..da7e814
--- /dev/null
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/wrapper/proxy/ProxyCreatorTestUsingCodegenPlugin.java
@@ -0,0 +1,97 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.core.wrapper.proxy;
+
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.isis.core.wrapper.handlers.DelegatingInvocationHandler;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ProxyCreatorTestUsingCodegenPlugin {
+
+    private ProxyCreator proxyCreator;
+
+    @Before
+    public void setUp() throws Exception {
+        proxyCreator = new ProxyCreator();
+    }
+
+    public static class Employee {
+        private String name;
+        public String getName() {
+            return name;
+        }
+        public void setName(final String name) {
+            this.name = name;
+        }
+    }
+
+    private static class DelegatingInvocationHandlerForTest implements DelegatingInvocationHandler<Employee> {
+        private final Employee delegate = new Employee();
+        private final Set<String> invoked = new HashSet<>();
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            invoked.add(method.getName());
+            return "hi";
+        }
+
+        @Override
+        public Employee getDelegate() {
+            return delegate;
+        }
+
+        @Override
+        public boolean isResolveObjectChangedEnabled() {
+            return false;
+        }
+
+        @Override
+        public void setResolveObjectChangedEnabled(boolean resolveObjectChangedEnabled) {
+        }
+
+        public boolean wasInvoked(String methodName) {
+            return invoked.contains(methodName);
+        }
+    }
+
+    @Test
+    public void proxyShouldDelegateCalls() {
+
+        final DelegatingInvocationHandlerForTest handler = new DelegatingInvocationHandlerForTest();
+        final Employee proxyOfEmployee = proxyCreator.instantiateProxy(handler);
+
+        Assert.assertNotNull(proxyOfEmployee);
+
+        Assert.assertNotEquals(Employee.class.getName(), proxyOfEmployee.getClass().getName());
+
+        Assert.assertFalse(handler.wasInvoked("getName"));
+
+        Assert.assertEquals("hi", proxyOfEmployee.getName());
+
+        Assert.assertTrue(handler.wasInvoked("getName"));
+
+    }
+
+}
diff --git a/core/unittestsupport-test/src/test/resources/signed.jar b/core/detached-tests/src/test/resources/signed.jar
similarity index 100%
rename from core/unittestsupport-test/src/test/resources/signed.jar
rename to core/detached-tests/src/test/resources/signed.jar
diff --git a/core/detached-tests/xml/objects/CUS.xml b/core/detached-tests/xml/objects/CUS.xml
new file mode 100644
index 0000000..e69de29
diff --git a/core/detached-tests/xml/objects/CUS/1.txt b/core/detached-tests/xml/objects/CUS/1.txt
new file mode 100644
index 0000000..e69de29
diff --git a/core/detached-tests/xml/objects/CUS/1.xml b/core/detached-tests/xml/objects/CUS/1.xml
new file mode 100644
index 0000000..e69de29
diff --git a/core/detached-tests/xml/objects/CUS/2.xml b/core/detached-tests/xml/objects/CUS/2.xml
new file mode 100644
index 0000000..e69de29
diff --git a/core/pom.xml b/core/pom.xml
index 9bcfddd..1d6d5d9 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -2523,7 +2523,7 @@ ${license.additional-notes}
         <module>plugins/security-shiro</module>
 
         <!-- to break cyclic dependencies some tests needed to be moved to their own modules -->
-        <module>unittestsupport-test</module>
+        <module>detached-tests</module>
 
         <module>webdocker</module>
     </modules>