@Test
  public void testMultiThreads() throws IOException {
    mappedPageFactory =
        new MappedPageFactoryImpl(1024 * 1024 * 128, testDir + "/test_multi_threads", 2 * 1000);

    int pageNumLimit = 200;
    int threadNum = 1000;

    Map<Integer, IMappedPage[]> sharedMap1 =
        this.testAndGetSharedMap(mappedPageFactory, threadNum, pageNumLimit);
    assertTrue(this.mappedPageFactory.getCacheSize() == pageNumLimit);
    Map<Integer, IMappedPage[]> sharedMap2 =
        this.testAndGetSharedMap(mappedPageFactory, threadNum, pageNumLimit);
    assertTrue(this.mappedPageFactory.getCacheSize() == pageNumLimit);
    // pages in two maps should be same since they are all cached
    verifyMap(sharedMap1, sharedMap2, threadNum, pageNumLimit, true);
    verifyClosed(sharedMap1, threadNum, pageNumLimit, false);

    TestUtil.sleepQuietly(2500);
    this.mappedPageFactory.acquirePage(pageNumLimit + 1); // trigger mark&sweep
    assertTrue(this.mappedPageFactory.getCacheSize() == 1);
    Map<Integer, IMappedPage[]> sharedMap3 =
        this.testAndGetSharedMap(mappedPageFactory, threadNum, pageNumLimit);
    assertTrue(this.mappedPageFactory.getCacheSize() == pageNumLimit + 1);
    // pages in two maps should be different since all pages in sharedMap1 has expired and purged
    // out
    verifyMap(sharedMap1, sharedMap3, threadNum, pageNumLimit, false);
    verifyClosed(sharedMap3, threadNum, pageNumLimit, false);

    verifyClosed(sharedMap1, threadNum, pageNumLimit, true);

    // ensure no memory leak
    assertTrue(((MappedPageFactoryImpl) mappedPageFactory).getLockMapSize() == 0);
  }
  @Test
  public void testSingleThread() throws IOException {

    mappedPageFactory =
        new MappedPageFactoryImpl(1024 * 1024 * 128, testDir + "/test_single_thread", 2 * 1000);

    IMappedPage mappedPage = mappedPageFactory.acquirePage(0); // first acquire
    assertNotNull(mappedPage);
    IMappedPage mappedPage0 = mappedPageFactory.acquirePage(0); // second acquire
    assertSame(mappedPage, mappedPage0);

    IMappedPage mappedPage1 = mappedPageFactory.acquirePage(1);
    assertNotSame(mappedPage0, mappedPage1);

    mappedPageFactory.releasePage(0); // release first acquire
    mappedPageFactory.releasePage(0); // release second acquire
    TestUtil.sleepQuietly(2200); // let page0 expire
    mappedPageFactory.acquirePage(2); // trigger mark&sweep and purge old page0
    mappedPage = mappedPageFactory.acquirePage(0); // create a new page0
    assertNotSame(mappedPage, mappedPage0);
    TestUtil.sleepQuietly(1000); // let the async cleaner do the job
    assertTrue(!mappedPage.isClosed());
    assertTrue(mappedPage0.isClosed());

    for (long i = 0; i < 100; i++) {
      assertNotNull(mappedPageFactory.acquirePage(i));
    }
    assertTrue(mappedPageFactory.getCacheSize() == 100);
    Set<Long> indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 100);
    for (long i = 0; i < 100; i++) {
      assertTrue(indexSet.contains(i));
    }

    this.mappedPageFactory.deletePage(0);
    assertTrue(mappedPageFactory.getCacheSize() == 99);
    indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 99);

    this.mappedPageFactory.deletePage(1);
    assertTrue(mappedPageFactory.getCacheSize() == 98);
    indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 98);

    for (long i = 2; i < 50; i++) {
      this.mappedPageFactory.deletePage(i);
    }
    assertTrue(mappedPageFactory.getCacheSize() == 50);
    indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 50);

    this.mappedPageFactory.deleteAllPages();
    assertTrue(mappedPageFactory.getCacheSize() == 0);
    indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 0);

    long start = System.currentTimeMillis();
    for (long i = 0; i < 5; i++) {
      assertNotNull(this.mappedPageFactory.acquirePage(i));
      TestUtil.sleepQuietly(1000);
    }
    indexSet = mappedPageFactory.getPageIndexSetBefore(start - 1000);
    assertTrue(indexSet.size() == 0);
    indexSet = mappedPageFactory.getPageIndexSetBefore(start + 2500);
    assertTrue(indexSet.size() == 3);
    indexSet = mappedPageFactory.getPageIndexSetBefore(start + 5000);
    assertTrue(indexSet.size() == 5);

    mappedPageFactory.deletePagesBefore(start + 2500);
    indexSet = mappedPageFactory.getExistingBackFileIndexSet();
    assertTrue(indexSet.size() == 2);
    assertTrue(mappedPageFactory.getCacheSize() == 2);

    mappedPageFactory.releaseCachedPages();
    assertTrue(mappedPageFactory.getCacheSize() == 0);

    assertTrue(((MappedPageFactoryImpl) mappedPageFactory).getLockMapSize() == 0);
    mappedPageFactory.deleteAllPages();

    start = System.currentTimeMillis();
    for (int i = 0; i <= 100; i++) {
      IMappedPage mappedPageI = mappedPageFactory.acquirePage(i);
      mappedPageI.getLocal(0).put(("hello " + i).getBytes());
      mappedPageI.setDirty(true);
      ((MappedPageImpl) mappedPageI).flush();
      long currentTime = System.currentTimeMillis();
      long iPageFileLastModifiedTime = mappedPageFactory.getPageFileLastModifiedTime(i);
      assertTrue(iPageFileLastModifiedTime >= start);
      assertTrue(iPageFileLastModifiedTime <= currentTime);

      long index = mappedPageFactory.getFirstPageIndexBefore(currentTime + 1);
      assertTrue(index == i);

      start = currentTime;
    }

    mappedPageFactory.deleteAllPages();

    // test wrapped case
    mappedPageFactory.acquirePage(Long.MAX_VALUE - 1);
    mappedPageFactory.acquirePage(Long.MAX_VALUE);
    long index = mappedPageFactory.getFirstPageIndexBefore(System.currentTimeMillis() + 1);
    assertTrue(index == Long.MAX_VALUE);

    mappedPageFactory.acquirePage(0);
    index = mappedPageFactory.getFirstPageIndexBefore(System.currentTimeMillis() + 1);
    assertTrue(index == 0);

    mappedPageFactory.acquirePage(1);
    index = mappedPageFactory.getFirstPageIndexBefore(System.currentTimeMillis() + 1);
    assertTrue(index == 1);
  }