blob: aa56369deeec63e2247ee0798e36546792fe0480 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.common.caching;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
/**
*/
public class CreatingCacheTest {
private static class FakeFactory implements CreatingCache.ValueFactory<String, String> {
@Override
@NonNull
public String create(@NonNull String key) {
return key;
}
}
private static class DelayedFactory implements CreatingCache.ValueFactory<String, String> {
@NonNull
private final CountDownLatch mLatch;
public DelayedFactory(@NonNull CountDownLatch latch) {
mLatch = latch;
}
@Override
@NonNull
public String create(@NonNull String key) {
try {
mLatch.await();
} catch (InterruptedException ignored) {
}
return key;
}
}
@Test
public void testSingleThread() throws Exception {
CreatingCache<String, String> cache = new CreatingCache<String, String>(new FakeFactory());
String value1 = cache.get("key");
assertEquals("key", value1);
String value2 = cache.get("key");
assertEquals("key", value2);
//noinspection StringEquality
assertTrue("repetitive calls give same instance", value1 == value2);
}
private static class CacheRunnable implements Runnable {
@NonNull
private final CreatingCache<String, String> mCache;
@Nullable
private final CountDownLatch mLatch;
private String mResult;
private InterruptedException mException;
CacheRunnable(@NonNull CreatingCache<String, String> cache) {
this(cache, null);
}
/**
* Creates a runnable, that will notify when it's pending on a query.
*
* @param cache the cache to query
* @param latch the latch to countdown when the query is being processed.
*/
CacheRunnable(
@NonNull CreatingCache<String, String> cache,
@Nullable CountDownLatch latch) {
mCache = cache;
mLatch = latch;
}
@Override
public void run() {
if (mLatch != null) {
mResult = mCache.get("foo", new CreatingCache.QueryListener() {
@Override
public void onQueryState(@NonNull CreatingCache.State state) {
mLatch.countDown();
}
});
} else {
mResult = mCache.get("foo");
}
}
public String getResult() {
return mResult;
}
public InterruptedException getException() {
return mException;
}
}
@Test
public void testMultiThread() throws Exception {
// the latch that controls whether the factory will "create" an item.
CountDownLatch factoryLatch = new CountDownLatch(1);
CreatingCache<String, String>
cache = new CreatingCache<String, String>(new DelayedFactory(factoryLatch));
// the latch that will be released when the runnable1 is pending its query.
CountDownLatch latch1 = new CountDownLatch(1);
CacheRunnable runnable1 = new CacheRunnable(cache, latch1);
Thread t1 = new Thread(runnable1);
t1.start();
// wait on thread1 being waiting on the query, before creating thread2
latch1.await();
// the latch that will be released when the runnable1 is pending its query.
CountDownLatch latch2 = new CountDownLatch(1);
CacheRunnable runnable2 = new CacheRunnable(cache,latch2);
Thread t2 = new Thread(runnable2);
t2.start();
// wait on thread2 being waiting on the query, before releasing the factory
latch2.await();
factoryLatch.countDown();
// wait on threads being done.
t1.join();
t2.join();
assertEquals("foo", runnable1.getResult());
assertEquals("foo", runnable2.getResult());
//noinspection StringEquality
assertTrue("repetitive calls give same instance", runnable1.getResult() == runnable2.getResult());
}
@Test(expected = IllegalStateException.class)
public void testClear() throws Exception {
// the latch that controls whether the factory will "create" an item.
// this is never released in this test since we want to try clearing the cache while an
// item is pending creation.
CountDownLatch factoryLatch = new CountDownLatch(1);
final CreatingCache<String, String>
cache = new CreatingCache<String, String>(new DelayedFactory(factoryLatch));
// the latch that will be released when the thread is pending its query.
CountDownLatch latch = new CountDownLatch(1);
new Thread(new CacheRunnable(cache, latch)).start();
// wait on thread to be waiting, before trying to clear the cache.
latch.await();
cache.clear();
}
}