package nxm.sys.test.junit.lib;

import nxm.sys.lib.*;
import nxm.sys.lib.Cache;
import nxm.sys.test.junit.NMTestCase;

/** This will test the Cache class. Only tests LRU at this time.
    @author  J. Czechowski IV
    @version $Id: CacheTest.java,v 1.7 2015/09/03 14:17:36 ntn Exp $
 */
public final class CacheTest extends NMTestCase {
  private static final String[] LRU_CACHE_TYPES = { "LRU", "SLRU" };

  /** public constructor to allow running this test case directly with JUnit. */
  public CacheTest (String methodName) { this(methodName, null); }

  /** Creates a new instance of the class.
      @param methodName The name of the method being called.
      @param midas      The midas context testTo use when running the tests.
   */
  public CacheTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  /** Adds a bunch of entries to the cache. */
  private void addEntriesToCache (Cache cache, int start, int end) {
    for (int i = start; i < end; i++) {
      cache.put("KEY_"+i, "VAL_"+i);
    }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////

  public void testGetCacheType() {
    Cache cache1 = new Cache(16);
    Cache cache2 = new Cache(Cache.LRU, 16);
    Cache cache3 = new Cache("LRU", 16, 3600.0);
    Cache cache4 = new Cache("SLRU", 16);
    Cache cache5 = new Cache("AUTO", 16);
    Cache cache6 = new Cache(Cache.SAUTO, 16);

    // test get cache type
    assertEquals("getCacheType: Type for cache1 (default) is SAUTO", Cache.SAUTO, cache1.getCacheType());
    assertEquals("getCacheType: Type for cache2 is LRU",   Cache.LRU,   cache2.getCacheType());
    assertEquals("getCacheType: Type for cache3 is LRU",   Cache.LRU,   cache3.getCacheType());
    assertEquals("getCacheType: Type for cache4 is SLRU",  Cache.SLRU,  cache4.getCacheType());
    assertEquals("getCacheType: Type for cache5 is AUTO",  Cache.AUTO,  cache5.getCacheType());
    assertEquals("getCacheType: Type for cache6 is SAUTO", Cache.SAUTO, cache6.getCacheType());

    // test get cache type name
    assertEquals("getCacheTypeName: Type for cache1 (default) is SAUTO", "SAUTO", cache1.getCacheTypeName());
    assertEquals("getCacheTypeName: Type for cache2 is LRU",   "LRU",   cache2.getCacheTypeName());
    assertEquals("getCacheTypeName: Type for cache3 is LRU",   "LRU",   cache3.getCacheTypeName());
    assertEquals("getCacheTypeName: Type for cache4 is SLRU",  "SLRU",  cache4.getCacheTypeName());
    assertEquals("getCacheTypeName: Type for cache5 is AUTO",  "AUTO",  cache5.getCacheTypeName());
    assertEquals("getCacheTypeName: Type for cache6 is SAUTO", "SAUTO", cache6.getCacheTypeName());
  }

  /** tested in {@link #testGetCacheType()}. */
  public void testGetCacheTypeName() {}

  public void testPeek() {
    testPeek(true);
    testPeek(false);
  }

  private void testPeek(boolean vc) {
    Cache cache1 = new Cache(Cache.LRU, 32, 0);
    addEntriesToCache(cache1, 0, 32);

    for (int i = 0; i < 10; i++) {
      if (vc) assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i));
              assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i,vc));
    }

    addEntriesToCache(cache1, 100, 110);

    for (int i = 0; i < 10; i++) {
      assertEquals("peek(KEY_"+i+") = VAL_"+i, null, cache1.peek("KEY_"+i,false));
    }
    for (int i = 10; i < 32; i++) {
      if (vc) assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i));
              assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i,vc));
    }
    for (int i = 100; i < 110; i++) {
      if (vc) assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i));
              assertEquals("peek(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.peek("KEY_"+i,vc));
    }
  }

  public void testGet() {
    testGet(true);
    testGet(false);
  }

  private void testGet(boolean vc) {
    for (String cacheType : LRU_CACHE_TYPES) {
      Cache cache1 = new Cache(cacheType, 32);
      addEntriesToCache(cache1, 0, 32);

      for (int i = 0; i < 10; i++) {
        if (vc) assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
                assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i,vc));
      }

      addEntriesToCache(cache1, 100, 110);

      for (int i = 0; i < 10; i++) {
        if (vc) assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
                assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i,vc));
      }
      for (int i = 10; i < 20; i++) {
        assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, null, cache1.get("KEY_"+i,false));
      }
      for (int i = 20; i < 32; i++) {
        if (vc) assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
                assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i,vc));
      }
      for (int i = 100; i < 110; i++) {
        if (vc) assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
                assertEquals(cacheType + ": get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i,vc));
      }
    } // for loop for LRU cache types
    // TODO: add tests for AUTO and SAUTO cache types
  }

  public void testRemove() {
    Cache cache1 = new Cache(32);
    addEntriesToCache(cache1, 0, 32);

    for (int i = 0; i < 10; i++) {
      assertEquals("Test remove: get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
    }

    cache1.remove("KEY_0");
    cache1.put("KEY_100", "VAL_100");

    assertEquals("Test remove: get(KEY_0) = null", null, cache1.get("KEY_0"));
    assertEquals("Test remove: get(KEY_100) = VAL_100", "VAL_100", cache1.get("KEY_100"));
    for (int i = 1; i < 32; i++) {
      assertEquals("Test remove: get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
    }
  }

  /** tested in {@link #testGet()}, {@link #testPeek()}, and {@link #testRemove()}. */
  public void testPut() {}

  public void testClear() {
    Cache cache1 = new Cache(32);
    addEntriesToCache(cache1, 0, 32);

    for (int i = 0; i < 10; i++) {
      assertEquals("Test clear: get(KEY_"+i+") = VAL_"+i, "VAL_"+i, cache1.get("KEY_"+i));
    }

    cache1.clear();

    for (int i = 0; i < 10; i++) {
      assertEquals("Test clear: get(KEY_"+i+") = VAL_"+i, null, cache1.get("KEY_"+i));
    }
  }

  public void testToString() {
    Cache cache1 = new Cache(16);
    assertTrue("Test toString: Starts with 'Cache:'", cache1.toString().startsWith("Cache:"));
  }

  public void testGetStats() {
    Cache cache1 = new Cache(32);
    assertNotNull("Test getStats: Got a Table", cache1.getStats());
  }

  public void testGetMaxSize() {
    Cache cache1 = new Cache(32);
    Cache cache2 = new Cache(64);
    assertEquals("cache1.getMaxSize()", 32, cache1.getMaxSize());
    assertEquals("cache2.getMaxSize()", 64, cache2.getMaxSize());
  }

  public void testGetMaxAge() {
    Cache cache1 = new Cache(16);
    Cache cache2 = new Cache(Cache.LRU, 32, 3600.0);
    assertEquals("cache1.getMaxAge() = 0",            0.0,                cache1.getMaxAge());
    assertEquals("cache1.getMaxAge() = NO_AGE_LIMIT", Cache.NO_AGE_LIMIT, cache1.getMaxAge());
    assertEquals("cache2.getMaxAge() = 3600",         3600.0,             cache2.getMaxAge());
  }

  public void testSetMaxSize() {
    Cache cache1 = new Cache(32);
    assertEquals("cache1.getMaxSize() = 32", 32, cache1.getMaxSize());
    cache1.setMaxSize(64);
    assertEquals("cache1.getMaxSize() = 64", 64, cache1.getMaxSize());
    cache1.setMaxSize(16);
    assertEquals("cache1.getMaxSize() = 16", 16, cache1.getMaxSize());

    addEntriesToCache(cache1, 0, 100); // since 3.6.0 tests Cache.ConcurrentHashMapCache when adding more than maxSize
    assertEquals("cache size should be limited to max size", 16, cache1.size()); // since 3.6.0
  }

  public void testSetMaxAge() {
    Cache cache1 = new Cache(16);
    assertEquals("cache1.getMaxAge() = 0",            Cache.NO_AGE_LIMIT, cache1.getMaxAge());
    cache1.setMaxAge(3600.0);
    assertEquals("cache2.getMaxAge() = 3600",         3600.0,             cache1.getMaxAge());
    cache1.setMaxAge(Cache.NO_AGE_LIMIT);
    assertEquals("cache1.getMaxAge() = NO_AGE_LIMIT", Cache.NO_AGE_LIMIT, cache1.getMaxAge());
  }
}
