public void testPropagatingWrites() throws Exception {
    // put something in the store
    cs.store(InternalEntryFactory.create("k1", "v1"));
    cs.store(InternalEntryFactory.create("k2", "v2", lifespan));

    int i = 1;
    for (CacheStore s : stores) {
      assert s.containsKey("k1") : "Key k1 missing on store " + i;
      assert s.containsKey("k2") : "Key k2 missing on store " + i;
      assert s.load("k1").getValue().equals("v1");
      assert s.load("k2").getValue().equals("v2");
      assert s.load("k1").getLifespan() == -1;
      assert s.load("k2").getLifespan() == lifespan;
      i++;
    }

    cs.remove("k1");

    for (CacheStore s : stores) {
      assert !s.containsKey("k1");
      assert s.containsKey("k2");
      assert s.load("k1") == null;
      assert s.load("k2").getValue().equals("v2");
      assert s.load("k2").getLifespan() == lifespan;
    }

    cs.clear();

    for (CacheStore s : stores) {
      assert !s.containsKey("k1");
      assert !s.containsKey("k2");
      assert s.load("k1") == null;
      assert s.load("k2") == null;
    }

    cs.store(InternalEntryFactory.create("k1", "v1"));
    cs.store(InternalEntryFactory.create("k2", "v2", lifespan));
    cs.store(InternalEntryFactory.create("k3", "v3", 1000)); // short lifespan!

    for (CacheStore s : stores) {
      assert s.containsKey("k1");
      assert s.containsKey("k2");
      assert s.containsKey("k3");
    }

    Thread.sleep(1100);

    cs.purgeExpired();

    for (CacheStore s : stores) {
      assert s.containsKey("k1");
      assert s.containsKey("k2");
      assert !s.containsKey("k3");
    }
  }
  public void testPropagatingStreams()
      throws IOException, ClassNotFoundException, CacheLoaderException {
    store2.store(InternalEntryFactory.create("k1", "v1"));
    store2.store(InternalEntryFactory.create("k2", "v2", lifespan));

    assert cs.containsKey("k1");
    assert cs.containsKey("k2");

    ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(byteStream);
    cs.toStream(new UnclosableObjectOutputStream(oos));
    oos.close();
    byteStream.close();
    cs.clear();

    assert !cs.containsKey("k1");
    assert !cs.containsKey("k2");
    for (CacheStore s : stores) {
      assert !s.containsKey("k1");
      assert !s.containsKey("k2");
    }

    cs.fromStream(
        new UnclosableObjectInputStream(
            new ObjectInputStream(new ByteArrayInputStream(byteStream.toByteArray()))));

    assert cs.containsKey("k1");
    assert cs.containsKey("k2");
    assert cs.load("k1").getValue().equals("v1");
    assert cs.load("k2").getValue().equals("v2");
    assert cs.load("k1").getLifespan() == -1;
    assert cs.load("k2").getLifespan() == lifespan;

    assert store2.containsKey("k1");
    assert store2.containsKey("k2");
    assert store2.load("k1").getValue().equals("v1");
    assert store2.load("k2").getValue().equals("v2");
    assert store2.load("k1").getLifespan() == -1;
    assert store2.load("k2").getLifespan() == lifespan;

    // store 1 has not been set up to support fetching state
    assert !store1.containsKey("k1");
    assert !store1.containsKey("k2");
  }
  public void testGetsFromMultipleSrcs() throws Exception {

    assert cs.load("k1") == null;
    assert cs.load("k2") == null;
    assert cs.load("k3") == null;
    assert cs.load("k4") == null;

    // k1 is on store1
    store1.store(InternalEntryFactory.create("k1", "v1"));

    assertEquals(cs.loadAll().size(), 1);
    // k2 is on store2
    store2.store(InternalEntryFactory.create("k2", "v2"));
    assertEquals(cs.loadAll().size(), 2);

    // k3 is on both
    store1.store(InternalEntryFactory.create("k3", "v3"));
    assertEquals(cs.loadAll().size(), 3);
    store2.store(InternalEntryFactory.create("k3", "v3"));
    assertEquals(cs.loadAll().size(), 3);

    // k4 is on neither

    assert cs.load("k1").getValue().equals("v1");
    assert cs.load("k2").getValue().equals("v2");
    assert cs.load("k3").getValue().equals("v3");
    assert cs.load("k4") == null;

    Set<InternalCacheEntry> all = cs.loadAll();

    assertEquals(all.size(), 3);
    Set<Object> expectedKeys = new HashSet<Object>();
    expectedKeys.add("k1");
    expectedKeys.add("k2");
    expectedKeys.add("k3");
    for (InternalCacheEntry a : all) assert expectedKeys.remove(a.getKey());

    assert expectedKeys.isEmpty();

    cs.remove("k3");

    assert !store1.containsKey("k3");
    assert !store2.containsKey("k3");
  }
  public void testPropagatingTwoPhaseCommit() throws Exception {
    List<Modification> list = new LinkedList<Modification>();
    list.add(new Store(InternalEntryFactory.create("k1", "v1")));
    list.add(new Store(InternalEntryFactory.create("k2", "v2", lifespan)));
    list.add(new Store(InternalEntryFactory.create("k3", "v3")));
    list.add(new Remove("k3"));
    list.add(new Clear());
    list.add(new Store(InternalEntryFactory.create("k4", "v4")));
    list.add(new Store(InternalEntryFactory.create("k5", "v5", lifespan)));
    list.add(new Store(InternalEntryFactory.create("k6", "v6")));
    list.add(new Remove("k6"));
    GlobalTransaction tx = gtf.newGlobalTransaction(null, false);
    cs.prepare(list, tx, false);

    CacheStore[] allStores = new CacheStore[] {cs, store1, store2}; // for iteration

    for (int i = 1; i < 7; i++) {
      for (CacheStore s : allStores) assert !s.containsKey("k" + i);
    }

    cs.commit(tx);

    for (int i = 1; i < 7; i++) {
      if (i < 4 || i == 6) {
        // these have been deleted
        for (CacheStore s : allStores) assert !s.containsKey("k" + i);
      } else {
        for (CacheStore s : allStores) {
          assert s.containsKey("k" + i);
          assert s.load("k" + i).getValue().equals("v" + i);
          assert s.load("k" + i).getLifespan() == (i == 5 ? lifespan : -1);
        }
      }
    }
  }