11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2011 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.collect;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
203ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffinimport static java.util.concurrent.TimeUnit.HOURS;
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
220888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.annotations.GwtCompatible;
230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.annotations.GwtIncompatible;
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.base.Function;
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.MapMaker.RemovalNotification;
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.MapMakerInternalMapTest.QueuingRemovalListener;
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.testing.NullPointerTester;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
290888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport junit.framework.TestCase;
300888a09821a98ac0680fad765217302858e70fa4Paul Duffin
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Map;
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Set;
330888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.concurrent.ConcurrentHashMap;
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ConcurrentMap;
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.CountDownLatch;
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ExecutorService;
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.Executors;
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.atomic.AtomicInteger;
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Charles Fry
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
430888a09821a98ac0680fad765217302858e70fa4Paul Duffin@GwtCompatible(emulated = true)
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class MapMakerTest extends TestCase {
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
460888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GwtIncompatible("NullPointerTester")
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNullParameters() throws Exception {
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NullPointerTester tester = new NullPointerTester();
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    tester.testAllPublicInstanceMethods(new MapMaker());
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
520888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GwtIncompatible("threads")
530888a09821a98ac0680fad765217302858e70fa4Paul Duffin
541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalNotification_clear() throws InterruptedException {
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // If a clear() happens while a computation is pending, we should not get a removal
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // notification.
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch computingLatch = new CountDownLatch(1);
590888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Function<String, String> computingFunction = new DelayingIdentityLoader<String>(computingLatch);
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    QueuingRemovalListener<String, String> listener = new QueuingRemovalListener<String, String>();
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @SuppressWarnings("deprecation") // test of deprecated code
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final ConcurrentMap<String, String> map = new MapMaker()
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .concurrencyLevel(1)
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .removalListener(listener)
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .makeComputingMap(computingFunction);
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // seed the map, so its segment's count > 0
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    map.put("a", "a");
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch computationStarted = new CountDownLatch(1);
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch computationComplete = new CountDownLatch(1);
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    new Thread(new Runnable() {
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      @Override public void run() {
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        computationStarted.countDown();
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        map.get("b");
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        computationComplete.countDown();
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }).start();
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // wait for the computingEntry to be created
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    computationStarted.await();
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    map.clear();
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // let the computation proceed
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    computingLatch.countDown();
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // don't check map.size() until we know the get("b") call is complete
871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    computationComplete.await();
881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // At this point, the listener should be holding the seed value (a -> a), and the map should
901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // contain the computed value (b -> b), since the clear() happened before the computation
911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // completed.
921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, listener.size());
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    RemovalNotification<String, String> notification = listener.remove();
941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals("a", notification.getKey());
951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals("a", notification.getValue());
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(1, map.size());
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals("b", map.get("b"));
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  // "Basher tests", where we throw a bunch of stuff at a Cache and check basic invariants.
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * This is a less carefully-controlled version of {@link #testRemovalNotification_clear} - this is
1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * a black-box test that tries to create lots of different thread-interleavings, and asserts that
1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * each computation is affected by a call to {@code clear()} (and therefore gets passed to the
1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * removal listener), or else is not affected by the {@code clear()} (and therefore exists in the
1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * map afterward).
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1090888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GwtIncompatible("threads")
1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testRemovalNotification_clear_basher() throws InterruptedException {
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // If a clear() happens close to the end of computation, one of two things should happen:
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // - computation ends first: the removal listener is called, and the map does not contain the
1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    //   key/value pair
1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // - clear() happens first: the removal listener is not called, and the map contains the pair
1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    CountDownLatch computationLatch = new CountDownLatch(1);
1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    QueuingRemovalListener<String, String> listener = new QueuingRemovalListener<String, String>();
1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @SuppressWarnings("deprecation") // test of deprecated code
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final Map<String, String> map = new MapMaker()
1211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .removalListener(listener)
1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .concurrencyLevel(20)
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        .makeComputingMap(new DelayingIdentityLoader<String>(computationLatch));
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int nThreads = 100;
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int nTasks = 1000;
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    int nSeededEntries = 100;
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Set<String> expectedKeys = Sets.newHashSetWithExpectedSize(nTasks + nSeededEntries);
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // seed the map, so its segments have a count>0; otherwise, clear() won't visit the in-progress
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // entries
1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < nSeededEntries; i++) {
1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      String s = "b" + i;
1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      map.put(s, s);
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      expectedKeys.add(s);
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final AtomicInteger computedCount = new AtomicInteger();
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch tasksFinished = new CountDownLatch(nTasks);
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < nTasks; i++) {
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      final String s = "a" + i;
1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      threadPool.submit(new Runnable() {
1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          map.get(s);
1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          computedCount.incrementAndGet();
1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          tasksFinished.countDown();
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      expectedKeys.add(s);
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    computationLatch.countDown();
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // let some computations complete
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    while (computedCount.get() < nThreads) {
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Thread.yield();
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    map.clear();
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    tasksFinished.await();
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // Check all of the removal notifications we received: they should have had correctly-associated
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // keys and values. (An earlier bug saw removal notifications for in-progress computations,
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // which had real keys with null values.)
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Map<String, String> removalNotifications = Maps.newHashMap();
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (RemovalNotification<String, String> notification : listener) {
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      removalNotifications.put(notification.getKey(), notification.getValue());
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals("Unexpected key/value pair passed to removalListener",
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notification.getKey(), notification.getValue());
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // All of the seed values should have been visible, so we should have gotten removal
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // notifications for all of them.
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    for (int i = 0; i < nSeededEntries; i++) {
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals("b" + i, removalNotifications.get("b" + i));
1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // Each of the values added to the map should either still be there, or have seen a removal
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    // notification.
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(expectedKeys, Sets.union(map.keySet(), removalNotifications.keySet()));
1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(Sets.intersection(map.keySet(), removalNotifications.keySet()).isEmpty());
1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1820888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GwtIncompatible("threads")
1831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  static final class DelayingIdentityLoader<T> implements Function<T, T> {
1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    private final CountDownLatch delayLatch;
1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    DelayingIdentityLoader(CountDownLatch delayLatch) {
1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      this.delayLatch = delayLatch;
1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override public T apply(T key) {
1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      awaitUninterruptibly(delayLatch);
1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      return key;
1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1950888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1960888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /*
1970888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * TODO(cpovirk): eliminate duplication between these tests and those in LegacyMapMakerTests and
1980888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * anywhere else
1990888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
2000888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2010888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /** Tests for the builder. */
2020888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public static class MakerTest extends TestCase {
2030888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testInitialCapacity_negative() {
2040888a09821a98ac0680fad765217302858e70fa4Paul Duffin      MapMaker maker = new MapMaker();
2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2060888a09821a98ac0680fad765217302858e70fa4Paul Duffin        maker.initialCapacity(-1);
2070888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
2080888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalArgumentException expected) {
2090888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2110888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2120888a09821a98ac0680fad765217302858e70fa4Paul Duffin    // TODO(cpovirk): enable when ready
2130888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void xtestInitialCapacity_setTwice() {
2140888a09821a98ac0680fad765217302858e70fa4Paul Duffin      MapMaker maker = new MapMaker().initialCapacity(16);
2150888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2160888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // even to the same value is not allowed
2170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        maker.initialCapacity(16);
2180888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
2190888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalArgumentException expected) {
2200888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2210888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2220888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2230888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @SuppressWarnings("deprecation") // test of deprecated method
2240888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testExpiration_setTwice() {
2253ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      MapMaker maker = new MapMaker().expireAfterWrite(1, HOURS);
2260888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2270888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // even to the same value is not allowed
2283ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin        maker.expireAfterWrite(1, HOURS);
2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
2300888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalStateException expected) {
2310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2330888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2340888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testMaximumSize_setTwice() {
2350888a09821a98ac0680fad765217302858e70fa4Paul Duffin      MapMaker maker = new MapMaker().maximumSize(16);
2360888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2370888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // even to the same value is not allowed
2380888a09821a98ac0680fad765217302858e70fa4Paul Duffin        maker.maximumSize(16);
2390888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
2400888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalStateException expected) {
2410888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2420888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2440888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testReturnsPlainConcurrentHashMapWhenPossible() {
2450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      Map<?, ?> map = new MapMaker()
2460888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .initialCapacity(5)
2470888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .makeMap();
2480888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertTrue(map instanceof ConcurrentHashMap);
2490888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2500888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2510888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2520888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /** Tests of the built map with maximumSize. */
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public static class MaximumSizeTest extends TestCase {
2540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testPut_sizeIsZero() {
2550888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ConcurrentMap<Object, Object> map =
2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin          new MapMaker().maximumSize(0).makeMap();
2570888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(0, map.size());
2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin      map.put(new Object(), new Object());
2590888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(0, map.size());
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2610888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2620888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testSizeBasedEviction() {
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin      int numKeys = 10;
2640888a09821a98ac0680fad765217302858e70fa4Paul Duffin      int mapSize = 5;
2650888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ConcurrentMap<Object, Object> map =
2660888a09821a98ac0680fad765217302858e70fa4Paul Duffin          new MapMaker().maximumSize(mapSize).makeMap();
2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin      for (int i = 0; i < numKeys; i++) {
2680888a09821a98ac0680fad765217302858e70fa4Paul Duffin        map.put(i, i);
2690888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(mapSize, map.size());
2710888a09821a98ac0680fad765217302858e70fa4Paul Duffin      for (int i = numKeys - mapSize; i < mapSize; i++) {
2720888a09821a98ac0680fad765217302858e70fa4Paul Duffin        assertTrue(map.containsKey(i));
2730888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2750888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2760888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2770888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /** Tests for recursive computation. */
2780888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public static class RecursiveComputationTest extends TestCase {
2790888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Function<Integer, String> recursiveComputer
2800888a09821a98ac0680fad765217302858e70fa4Paul Duffin        = new Function<Integer, String>() {
2810888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override
2820888a09821a98ac0680fad765217302858e70fa4Paul Duffin      public String apply(Integer key) {
2830888a09821a98ac0680fad765217302858e70fa4Paul Duffin        if (key > 0) {
2840888a09821a98ac0680fad765217302858e70fa4Paul Duffin          return key + ", " + recursiveMap.get(key - 1);
2850888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } else {
2860888a09821a98ac0680fad765217302858e70fa4Paul Duffin          return "0";
2870888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
2880888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2890888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
2900888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ConcurrentMap<Integer, String> recursiveMap = new MapMaker()
2920888a09821a98ac0680fad765217302858e70fa4Paul Duffin        .makeComputingMap(recursiveComputer);
2930888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testRecursiveComputation() {
2950888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals("3, 2, 1, 0", recursiveMap.get(3));
2960888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2970888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2980888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2990888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
3000888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * Tests for computing functionality.
3010888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
3020888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public static class ComputingTest extends TestCase {
3030888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testComputerThatReturnsNull() {
3040888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ConcurrentMap<Integer, String> map = new MapMaker()
3050888a09821a98ac0680fad765217302858e70fa4Paul Duffin          .makeComputingMap(new Function<Integer, String>() {
3060888a09821a98ac0680fad765217302858e70fa4Paul Duffin            @Override
3070888a09821a98ac0680fad765217302858e70fa4Paul Duffin            public String apply(Integer key) {
3080888a09821a98ac0680fad765217302858e70fa4Paul Duffin              return null;
3090888a09821a98ac0680fad765217302858e70fa4Paul Duffin            }
3100888a09821a98ac0680fad765217302858e70fa4Paul Duffin          });
3110888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
3120888a09821a98ac0680fad765217302858e70fa4Paul Duffin        map.get(1);
3130888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
3140888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (NullPointerException e) { /* expected */ }
3150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
3160888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3170888a09821a98ac0680fad765217302858e70fa4Paul Duffin    public void testRuntimeException() {
3180888a09821a98ac0680fad765217302858e70fa4Paul Duffin      final RuntimeException e = new RuntimeException();
3190888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3200888a09821a98ac0680fad765217302858e70fa4Paul Duffin      ConcurrentMap<Object, Object> map = new MapMaker().makeComputingMap(
3210888a09821a98ac0680fad765217302858e70fa4Paul Duffin          new Function<Object, Object>() {
3220888a09821a98ac0680fad765217302858e70fa4Paul Duffin        @Override
3230888a09821a98ac0680fad765217302858e70fa4Paul Duffin        public Object apply(Object from) {
3240888a09821a98ac0680fad765217302858e70fa4Paul Duffin          throw e;
3250888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
3260888a09821a98ac0680fad765217302858e70fa4Paul Duffin      });
3270888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3280888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
3290888a09821a98ac0680fad765217302858e70fa4Paul Duffin        map.get(new Object());
3300888a09821a98ac0680fad765217302858e70fa4Paul Duffin        fail();
3310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (ComputationException ce) {
3320888a09821a98ac0680fad765217302858e70fa4Paul Duffin        assertSame(e, ce.getCause());
3330888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3340888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
3350888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
3361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
337