/* * 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; } }