/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008 jOpenDocument, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU
* General Public License Version 3 only ("GPL").
* You may not use this file except in compliance with the License.
* You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
* See the License for the specific language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*
*/
package org.jopendocument.util.cache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.Thread.State;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class ICacheTest {
private final ThreadGroup thg = new ThreadGroup("testjunit") {
public void uncaughtException(Thread t, Throwable e) {
ICacheTest.this.threadException = e;
}
};
private Thread launch(String name, Runnable runnable) {
final Thread res = new Thread(this.thg, runnable, name);
res.start();
// really let res start...
Thread.yield();
return res;
}
private void join(final Thread t) throws Throwable {
t.join();
if (this.threadException != null) {
final Throwable thw = this.threadException;
this.threadException = null;
throw thw;
}
}
private static final int delay = 1;
private ICache<String, Object, Object> cache;
protected Throwable threadException;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
}
@Before
public void setUp() throws Exception {
this.cache = new ICache<String, Object, Object>(delay);
}
@After
public void tearDown() throws Exception {
this.cache = null;
}
@Test
public void testWatcher() throws NoSuchMethodException {
class BogusCacheWatcher<K, D> extends CacheWatcher<K, D> {
public BogusCacheWatcher(ICache<K, ?, D> c, D d) {
super(c, d);
}
public void clear() {
this.clearCache();
}
}
final ICache<String, List, String> cache = new ICache<String, List, String>(delay);
cache.setWatcherFactory(new CacheWatcherFactory<String, String>() {
public CacheWatcher<String, String> createWatcher(ICache<String, ?, String> cache, String obj) throws Exception {
return new BogusCacheWatcher<String, String>(cache, obj);
}
});
final String sel = "SELECT * FROM CONTACT";
final String sel2 = "SELECT NOM FROM CONTACT";
final List r = new ArrayList();
final String data = "CONTACT_TABLE";
assertFalse(cache.dependsOn(data));
// put both in cache
final Set<? extends CacheWatcher<String, String>> put = cache.put(sel, r, Collections.singleton(data));
final BogusCacheWatcher<String, String> cw = (BogusCacheWatcher<String, String>) put.iterator().next();
cache.put(sel2, r, Collections.singleton(data));
// now cache depends on data
assertTrue(cache.dependsOn(data));
cache.clear(sel2);
// still depends on data because of sel
assertTrue(cache.dependsOn(data));
assertSame(r, cache.get(sel).getRes());
// apres une modif, le cache est invalidé
cw.clear();
assertSame(CacheResult.State.NOT_IN_CACHE, cache.get(sel).getState());
assertFalse(cache.dependsOn(data));
}
@Test
public void testInterrupt() throws Throwable {
// test that we can interrupt a waiting thread
this.getCache().addRunning("SELECT *");
final Thread tInt = launch("test interrupt", new Runnable() {
public void run() {
final CacheResult<Object> res = getCache().get("SELECT *");
assertSame(CacheResult.State.INTERRUPTED, res.getState());
}
});
tInt.interrupt();
join(tInt);
this.getCache().removeRunning("SELECT *");
}
@Test
public void testGet() throws Throwable {
// cache vide
assertEquals(0, this.getCache().size());
assertSame(CacheResult.State.NOT_IN_CACHE, this.getCache().get("SELECT 1").getState());
this.getCache().put("SELECT 1", new Integer(1), Collections.singleton(1));
// test timeout
final Thread t = launch("test timeout", new Runnable() {
public void run() {
try {
Thread.sleep((long) (delay * 1000 * 1.1));
} catch (InterruptedException e) {
e.printStackTrace();
fail(e.getMessage());
}
assertSame(CacheResult.State.NOT_IN_CACHE, getCache().get("SELECT 1").getState());
}
});
assertEquals(new Integer(1), this.getCache().get("SELECT 1").getRes());
join(t);
}
@Test
public void testRunning() throws Throwable {
final String sel = "SELECT 2";
assertFalse(this.getCache().isRunning(sel));
this.getCache().addRunning(sel);
assertTrue(this.getCache().isRunning(sel));
final Vector<Object> t2Res = new Vector<Object>();
final CountDownLatch latch = new CountDownLatch(1);
final Thread t2 = launch("test running", new Runnable() {
public void run() {
t2Res.add("begun");
latch.countDown();
t2Res.add(getCache().get(sel).getRes());
}
});
// be sure that t2 started
latch.await();
assertEquals(1, t2Res.size());
// and that it is waiting (give t2 some time to get to "get(sel)")
Thread.sleep(20);
assertEquals(State.WAITING, t2.getState());
final Object two = new Object();
assertTrue(getCache().isRunning(sel));
this.getCache().put(sel, two, Collections.emptySet());
assertFalse(getCache().isRunning(sel));
join(t2);
// assert that t2 has been given our two
assertSame(two, t2Res.get(1));
}
@Test
public void testCheck() {
final Object nul = new Object();
this.getCache().put("SELECT NULL", nul, Collections.emptySet());
assertSame(nul, this.getCache().check("SELECT NULL").getRes());
// it should be in cache
assertFalse(this.getCache().isRunning("SELECT NULL"));
}
@Test
public void testSizeAndClear() {
final ICache<String, String, Object> cache = new ICache<String, String, Object>(600, 1);
cache.put("a", "A", Collections.emptySet());
assertEquals(1, cache.size());
cache.put("b", "B", Collections.emptySet());
assertEquals(1, cache.size());
assertSame(CacheResult.State.NOT_IN_CACHE, cache.get("a").getState());
}
@Test
public void testClear() {
this.getCache().put("a", "A", Collections.emptySet());
assertEquals(1, this.getCache().size());
this.getCache().clear("a");
assertEquals(0, this.getCache().size());
// we can remove something that isn't there
this.getCache().clear("b");
assertEquals(0, this.getCache().size());
}
private ICache<String, Object, Object> getCache() {
return this.cache;
}
}