/** Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved. Contact: SYSTAP, LLC DBA Blazegraph 2501 Calvert ST NW #106 Washington, DC 20008 licenses@blazegraph.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Created on Apr 21, 2006 */ package com.bigdata.cache; /** * Tests suite for the {@link WeakValueCache}. This class tests the weak cache * and its integration with a delegate hard reference cache policy. * * @todo verify entries cleared once weakly reachable, but not before. * * @todo make sure that the dirty flag is correctly maintained as entries are * cleared and removed from the weak value cache (i.e., that we do not * falsely apply the old state of the flag when recycling cache entries). * * @todo performance test suite for tuning access paths in the cache * implementation, including the load factor on the weak reference and * hard reference caches. Is it possible to write this test so that it can * be reused when a persistence layer is integrated? * * @todo integration test with abstraction for the specific persistence layer, * but never the less testing the semantics of the weak reference cache * with the integrated persistence layer. The abstract could consist of * the means for configuring the persistence layer with a known cache * configuration. * * @version $Id$ * @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a> */ public class TestWeakValueCache extends AbstractCachePolicyTest { /** * */ public TestWeakValueCache() { super(); } /** * @param name */ public TestWeakValueCache(String name) { super(name); } /** * Test legal and illegal invocations of the constructors. */ public void test_ctor() { // local fixtures. LRUCache<Integer,Object> delegate = new LRUCache<Integer,Object>(5); WeakCacheEntryFactory<Integer,Object> factory = new WeakCacheEntryFactory<Integer,Object>(); int initialCapacity = 1; float loadFactor = 1f; try { new WeakValueCache<Integer,Object>(null); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } new WeakValueCache<Integer,Object>(delegate); try { new WeakValueCache<Integer,Object>(delegate,null); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } new WeakValueCache<Integer,Object>(delegate,factory); /* * (initialCapacity,loadFactor,delegate,factory) */ try { new WeakValueCache<Integer,Object>(-1,.75f,delegate,factory); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } try { new WeakValueCache<Integer,Object>(initialCapacity,0f,delegate,factory); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } try { new WeakValueCache<Integer,Object>(initialCapacity,loadFactor,null,factory); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } try { new WeakValueCache<Integer,Object>(initialCapacity,loadFactor,delegate,null); fail("Expecting exception"); } catch( IllegalArgumentException ex ) { log.info("Ignoring expected exception: "+ex); } new WeakValueCache<Integer,Object>(initialCapacity,loadFactor,delegate,factory); } /** * Test fixture factory. * * @return A new {@link WeakValueCache} backed by a {@link LRUCache} with * the stated capacity. */ public ICachePolicy getCachePolicy(int capacity ) { return new WeakValueCache<Long,String>(new LRUCache<Long,String>(capacity)); } /** * Test verifies that put() may not be used to replace the object in the * cache under a given oid, but only to update the dirty flag associated * with that entry (and to update the LRU cache ordering). */ public void test_put_mayNotModifyObject() { String A = "A"; String B = "B"; WeakValueCache<Integer, String> cache = new WeakValueCache<Integer, String>( new LRUCache<Integer, String>(5)); cache.put(0,A,true); cache.put(0,A,false); try { cache.put(0,B,true); fail("Expecting exception."); } catch(IllegalStateException ex) { log.info("Ignoring expected exception: "+ex); } } /** * Tests excercises the ability to set and get the cache listener. */ public void test_cacheListener_getSet() { WeakValueCache<Integer,String> cache = new WeakValueCache<Integer,String>(new LRUCache<Integer,String>(1)); assertNull(cache.getCacheListener()); cache.setListener(null); assertNull(cache.getCacheListener()); ICacheListener<Integer,String> l = new MyCacheListenerThrowsException<Integer,String>(); cache.setListener( l ); assertEquals( l, cache.getCacheListener() ); cache.setListener(null); assertNull(cache.getCacheListener()); } /** * Test verifies that cache evictions are fired once the inner LRU cache is * full. */ public void test_cacheListener_objectEvicted() { /* * Setup weak cache backed by an LRU with a capacity of ONE (1). */ WeakValueCache<Integer, String> cache = new WeakValueCache<Integer, String>( new LRUCache<Integer, String>(1)); MyCacheListener<Integer, String> l = new MyCacheListener<Integer, String>(); l.denyEvents(); cache.setListener( l ); /* * Hold hard references to the objects that we insert into the weak * cache for this test. */ final String A = "A"; final String B = "B"; /* * Add an object to the cache under a key. */ cache.put(0, A, false); assertSameEntryOrdering( "contents", new CacheEntry[] { new CacheEntry<Integer, String>( 0, A, false) }, cache.entryIterator()); /* * Update the dirty flag for object under that key, so no eviction. */ cache.put(0, A, true); assertSameEntryOrdering( "contents", new CacheEntry[] { new CacheEntry<Integer, String>(0, A, true) }, cache.entryIterator()); /* * Add an object under another key, causes eviction of [A]. */ l.setExpectedEvent(0,A,true); cache.put(1, B, false ); l.clearLastEvent(); assertSameEntryOrdering("contents", new CacheEntry[] { new CacheEntry<Integer, String>( 1, B, false) }, cache.entryIterator()); /* * Update the dirty flag for the object under that key, so no eviction. */ l.denyEvents(); cache.put(1, B, true ); assertSameEntryOrdering("contents", new CacheEntry[] { new CacheEntry<Integer, String>( 1, B, true) }, cache.entryIterator()); /* * Add an object under another key, causes eviction of [D]. */ l.setExpectedEvent(1,B,true); cache.put(0, A, false ); l.clearLastEvent(); assertSameEntryOrdering("contents", new CacheEntry[] { new CacheEntry<Integer, String>( 0, A, false) }, cache.entryIterator()); } /** * Test verifies that changes to the dirty flag are propagated to the hard * reference cache. */ public void test_dirtyFlagPropagatesToHardReferenceCache() { LRUCache<Integer,String> lru = new LRUCache<Integer,String>(3); WeakValueCache<Integer,String> cache = new WeakValueCache<Integer,String>(lru); final String A = "A"; cache.put(0, A, false); assertSameEntryOrdering("contents", new CacheEntry[] { new CacheEntry<Integer, String>( 0, A, false) }, cache.entryIterator()); cache.put(0, A, true ); assertSameEntryOrdering("contents", new CacheEntry[] { new CacheEntry<Integer, String>( 0, A, true) }, cache.entryIterator()); } }