### Question:
ThreadSafeCache { public V getValue(final K key, final Callable<V> callable) throws InterruptedException, ExecutionException { return getValue(key, callable, defaultTimeoutFunction); } ThreadSafeCache(final ScheduledExecutorService executorService); void setDefaultTimeout(final Duration defaultTimeout); V getValue(final K key, final Callable<V> callable); V getValue(final K key,
                      final Callable<V> callable,
                      final AsyncFunction<V, Duration, Exception> expire); void clear();  }### Answer:
@Test public void shouldNotComputeValueMoreThanTenTimes() throws Exception { final Random random = new Random(); final AtomicInteger count = new AtomicInteger(); ExecutorService executorService = Executors.newFixedThreadPool(NUMBER_OF_THREADS); for (int i = 0; i < INVOCATION_COUNT; i++) { executorService.submit(new Callable<Object>() { @Override public Object call() throws Exception { final int value = Math.abs(random.nextInt() % NUMBER_OF_ENTRIES); cache.getValue(value, new Callable<Integer>() { @Override public Integer call() throws Exception { Thread.sleep(value * 5); count.incrementAndGet(); return value; } }); return null; } }); } executorService.shutdown(); executorService.awaitTermination(20L, TimeUnit.SECONDS); assertThat(count.get()).isEqualTo(NUMBER_OF_ENTRIES); }
@Test @SuppressWarnings("unchecked") public void shouldRegisterAnExpirationCallbackWithAppropriateDuration() throws Exception { cache.getValue(42, getCallable()); verify(executorService).schedule(any(Callable.class), delayCaptor.capture(), timeUnitCaptor.capture()); assertThat(delayCaptor.getValue()).isEqualTo(DEFAULT_CACHE_TIMEOUT.getValue()); assertThat(timeUnitCaptor.getValue()).isEqualTo(DEFAULT_CACHE_TIMEOUT.getUnit()); }
@Test @SuppressWarnings("unchecked") public void shouldOverrideDefaultTimeout() throws Exception { final Duration lowerDuration = duration("10 seconds"); cache.getValue(42, getCallable(), new AsyncFunction<Integer, Duration, Exception>() { @Override public Promise<Duration, Exception> apply(Integer value) throws Exception { return newResultPromise(lowerDuration); } }); verify(executorService).schedule(any(Callable.class), delayCaptor.capture(), timeUnitCaptor.capture()); assertThat(delayCaptor.getValue()).isEqualTo(lowerDuration.getValue()); assertThat(timeUnitCaptor.getValue()).isEqualTo(lowerDuration.getUnit()); }