Index: core/src/test/java/org/infinispan/asymmetric/AbstractAsymmetricClusterTest.java
===================================================================
--- core/src/test/java/org/infinispan/asymmetric/AbstractAsymmetricClusterTest.java	(revision 0)
+++ core/src/test/java/org/infinispan/asymmetric/AbstractAsymmetricClusterTest.java	(revision 0)
@@ -0,0 +1,113 @@
+package org.infinispan.asymmetric;
+
+import org.infinispan.Cache;
+import org.infinispan.config.Configuration;
+import org.infinispan.manager.CacheManager;
+import org.infinispan.test.MultipleCacheManagersTest;
+import org.infinispan.test.fwk.TestCacheManagerFactory;
+
+/**
+ * Emulate case where one of the caches act as a client for distributed cluster. Resembles very
+ * closely GUI demo configuration.
+ * 
+ * @author Juha Heljoranta
+ */
+abstract class AbstractAsymmetricClusterTest extends MultipleCacheManagersTest {
+
+   private static final int SLEEP_IN_MILLIS = 1;
+
+   // as of 2009-12-05 (r1253) tests will pass if this is too low, say 64.
+   private final int cacheEntriesNum = 1024;
+
+   protected int CACHES_NUM = 4;
+
+   private void adjustConfiguration(Configuration c) {
+      c.setL1CacheEnabled(true);
+      c.setL1Lifespan(60000);
+      c.setNumOwners(2);
+      c.setRehashRpcTimeout(120000);
+   }
+
+   @Override
+   protected void createCacheManagers() throws Throwable {
+      CacheManager[] cacheManagers = new CacheManager[CACHES_NUM];
+      for (int i = 0; i < CACHES_NUM; i++) {
+         cacheManagers[i] = TestCacheManagerFactory.createClusteredCacheManager();
+         // By using GlobalConfiguration.getClusteredDefault() behaves really bad from performance
+         // perspective
+         // GlobalConfiguration gc = GlobalConfiguration.getClusteredDefault();
+         // gc.setClusterName("testCluster");
+         // cacheManagers[i] = new DefaultCacheManager(gc);
+      }
+      registerCacheManager(cacheManagers);
+
+      Configuration[] configurations = new Configuration[CACHES_NUM];
+      for (int i = 0; i < CACHES_NUM; i++) {
+         configurations[i] = getDefaultClusteredConfig(Configuration.CacheMode.DIST_SYNC);
+         adjustConfiguration(configurations[i]);
+      }
+
+      /*
+       * The fist of the caches is the "client".
+       */
+      manipulateClientConfiguration(configurations[0]);
+
+      for (int i = 0; i < CACHES_NUM; i++) {
+         CacheManager cacheManager = cacheManagers[i];
+         Configuration configuration = configurations[i];
+         cacheManager.defineConfiguration(getCacheName(), configuration);
+      }
+
+      // start caches
+      for (int i = 0; i < CACHES_NUM; i++) {
+         Cache cache = cache(i, getCacheName());
+      }
+   }
+
+   protected abstract String getCacheName();
+
+   /**
+    * Adjust cache configuration. Configuration is used to interact with rest of the grid caches.
+    * 
+    * @param c
+    */
+   protected abstract void manipulateClientConfiguration(Configuration c);
+
+   /**
+    * Uses the first cache to interact with the cluster. Inserts {@link #cacheEntriesNum} values
+    * into grid and finally verifies that each of the inserted values still exists. Test fails if
+    * one or more inserted values are missing.
+    * 
+    * @throws InterruptedException
+    */
+   protected void performTest() throws InterruptedException {
+      Cache cache = cache(0, getCacheName());
+      for (int i = 0; i < cacheEntriesNum; i++) {
+         String key = getCacheName() + "-" + Integer.toString(i);
+         System.out.println("putting " + key);
+         cache.put(key, new byte[1]);
+         sleep();
+      }
+      for (int i = 0; i < cacheEntriesNum; i++) {
+         String key = getCacheName() + "-" + Integer.toString(i);
+         System.out.println("getting " + key);
+         assert cache.get(key) != null;
+         sleep();
+      }
+   }
+
+   /**
+    * Take a quick nap;
+    * 
+    * @throws InterruptedException
+    */
+   private void sleep() throws InterruptedException {
+      if (SLEEP_IN_MILLIS > 0) {
+         long start = System.currentTimeMillis();
+         do {
+            Thread.sleep(SLEEP_IN_MILLIS);
+         } while (System.currentTimeMillis() < start + SLEEP_IN_MILLIS);
+      }
+   }
+
+}
Index: core/src/test/java/org/infinispan/asymmetric/L1DisabledAsymmetricClusterTest.java
===================================================================
--- core/src/test/java/org/infinispan/asymmetric/L1DisabledAsymmetricClusterTest.java	(revision 0)
+++ core/src/test/java/org/infinispan/asymmetric/L1DisabledAsymmetricClusterTest.java	(revision 0)
@@ -0,0 +1,34 @@
+package org.infinispan.asymmetric;
+
+import org.infinispan.config.Configuration;
+import org.testng.annotations.Test;
+
+/**
+ * Cache interacting with the grid has l1 disabled.
+ * 
+ * @author Juha Heljoranta
+ */
+@Test(groups = "functional", testName = "asymmetric.L1DisabledAsymmetricClusterTest")
+public class L1DisabledAsymmetricClusterTest extends AbstractAsymmetricClusterTest {
+
+   @Override
+   protected String getCacheName() {
+      return L1DisabledAsymmetricClusterTest.class.getSimpleName();
+   }
+
+   @Override
+   protected void manipulateClientConfiguration(Configuration c) {
+      c.setL1CacheEnabled(false);
+      c.setL1Lifespan(1);
+   }
+
+   /**
+    * Test that the grid doesn't loose data even though interacting node has l1 disabled (numOwners
+    * should prevent data loss)
+    * 
+    * @throws InterruptedException
+    */
+   public void testL1Disabled() throws InterruptedException {
+      performTest();
+   }
+}
Index: core/src/test/java/org/infinispan/asymmetric/EvictingAsymmetricClusterTest.java
===================================================================
--- core/src/test/java/org/infinispan/asymmetric/EvictingAsymmetricClusterTest.java	(revision 0)
+++ core/src/test/java/org/infinispan/asymmetric/EvictingAsymmetricClusterTest.java	(revision 0)
@@ -0,0 +1,36 @@
+package org.infinispan.asymmetric;
+
+import org.infinispan.config.Configuration;
+import org.infinispan.eviction.EvictionStrategy;
+import org.testng.annotations.Test;
+
+/**
+ * Cache interacting with the grid has aggressive FIFO eviction policy.
+ * 
+ * @author Juha Heljoranta
+ */
+@Test(groups = "functional", testName = "asymmetric.EvictingAsymmetricClusterTest")
+public class EvictingAsymmetricClusterTest extends AbstractAsymmetricClusterTest {
+
+   @Override
+   protected String getCacheName() {
+      return EvictingAsymmetricClusterTest.class.getSimpleName();
+   }
+
+   @Override
+   protected void manipulateClientConfiguration(Configuration c) {
+      c.setEvictionStrategy(EvictionStrategy.FIFO);
+      c.setEvictionMaxEntries(8);
+   }
+
+   /**
+    * Test that the grid doesn't loose data even though single node evicts values (numOwners should
+    * prevent data loss).
+    * 
+    * @throws InterruptedException
+    */
+   public void testEvicting() throws InterruptedException {
+      performTest();
+   }
+
+}
Index: core/src/test/java/org/infinispan/asymmetric/NonAsymmetricClusterTest.java
===================================================================
--- core/src/test/java/org/infinispan/asymmetric/NonAsymmetricClusterTest.java	(revision 0)
+++ core/src/test/java/org/infinispan/asymmetric/NonAsymmetricClusterTest.java	(revision 0)
@@ -0,0 +1,33 @@
+package org.infinispan.asymmetric;
+
+import org.infinispan.config.Configuration;
+import org.testng.annotations.Test;
+
+/**
+ * Replicates GUI demo setup where each cache is configured identically. Test should pass and
+ * intention is to act as a reference test for other test in this package.
+ * 
+ * @author Juha Heljoranta
+ */
+@Test(groups = "functional", testName = "asymmetric.NonAsymmetricClusterTest")
+public class NonAsymmetricClusterTest extends AbstractAsymmetricClusterTest {
+
+   @Override
+   protected String getCacheName() {
+      return NonAsymmetricClusterTest.class.getSimpleName();
+   }
+
+   @Override
+   protected void manipulateClientConfiguration(Configuration c) {
+      // no-op, l1 is enabled by default
+   }
+
+   /**
+    * Test that the grid doesn't loose data.
+    * 
+    * @throws InterruptedException
+    */
+   public void testL1Enabled() throws InterruptedException {
+      performTest();
+   }
+}
Index: core/src/test/java/org/infinispan/asymmetric/EvictingL1DisabledAsymmetricClusterTest.java
===================================================================
--- core/src/test/java/org/infinispan/asymmetric/EvictingL1DisabledAsymmetricClusterTest.java	(revision 0)
+++ core/src/test/java/org/infinispan/asymmetric/EvictingL1DisabledAsymmetricClusterTest.java	(revision 0)
@@ -0,0 +1,37 @@
+package org.infinispan.asymmetric;
+
+import org.infinispan.config.Configuration;
+import org.infinispan.eviction.EvictionStrategy;
+import org.testng.annotations.Test;
+
+/**
+ * Cache interacting with the grid has aggressive FIFO eviction policy and l1 disabled.
+ * 
+ * @author Juha Heljoranta
+ */
+@Test(groups = "functional", testName = "asymmetric.EvictingL1DisabledAsymmetricClusterTest")
+public class EvictingL1DisabledAsymmetricClusterTest extends AbstractAsymmetricClusterTest {
+
+   @Override
+   protected String getCacheName() {
+      return EvictingL1DisabledAsymmetricClusterTest.class.getSimpleName();
+   }
+
+   @Override
+   protected void manipulateClientConfiguration(Configuration c) {
+      c.setL1CacheEnabled(false);
+      c.setL1Lifespan(1);
+      c.setEvictionStrategy(EvictionStrategy.FIFO);
+      c.setEvictionMaxEntries(8);
+   }
+
+   /**
+    * Test that the grid doesn't loose data even though single node evicts values and has l1
+    * disabled (numOwners should prevent data loss).
+    * 
+    * @throws InterruptedException
+    */
+   public void testL1DisabledAndEvicting() throws InterruptedException {
+      performTest();
+   }
+}
