Tests for storage manager

This commit is contained in:
Andrey Breslav
2013-10-02 17:11:30 +04:00
parent 0591ceb7f0
commit aa90302d1b
2 changed files with 460 additions and 1 deletions
@@ -0,0 +1,456 @@
package org.jetbrains.jet.storage;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import junit.framework.TestCase;
import org.jetbrains.jet.utils.WrappedValues;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.jetbrains.jet.storage.StorageManager.ReferenceKind.STRONG;
public class StorageManagerTest extends TestCase {
private StorageManager m;
@Override
public void setUp() throws Exception {
super.setUp();
m = new LockBasedStorageManager();
}
public static <T> void doTestComputesOnce(Computable<T> v, T expected, Counter counter) throws Exception {
assert 0 == counter.getCount();
T result1 = v.compute();
T result2 = v.compute();
assertEquals(1, counter.getCount());
assertEquals(expected, result1);
assertEquals(result1, result2);
}
public static <T> void doTestExceptionPreserved(Computable<T> v, Class<? extends Throwable> expected, Counter counter)
throws Exception {
assert 0 == counter.getCount();
Throwable caught1 = null;
try {
v.compute();
fail();
}
catch (Throwable e) {
caught1 = e;
}
Throwable caught2 = null;
try {
v.compute();
fail();
}
catch (Throwable e) {
caught2 = e;
}
assertEquals(1, counter.getCount());
assertTrue("Wrong exception class: " + caught1, expected.isInstance(caught1));
assertSame(caught1, caught2);
}
// Functions
public void testIsComputed() throws Exception {
NotNullLazyValue<String> value = m.createLazyValue(new CounterValue());
assertFalse(value.isComputed());
value.compute();
assertTrue(value.isComputed());
}
public void testIsNullableComputed() throws Exception {
NullableLazyValue<String> value = m.createNullableLazyValue(new CounterValueNull());
assertFalse(value.isComputed());
value.compute();
assertTrue(value.isComputed());
}
public void testIsComputedAfterException() throws Exception {
NotNullLazyValue<String> value = m.createLazyValue(new ExceptionCounterValue());
assertFalse(value.isComputed());
try {
value.compute();
}
catch (Exception ignored) {
}
assertTrue(value.isComputed());
}
public void testIsNullableComputedAfterException() throws Exception {
NullableLazyValue<String> value = m.createNullableLazyValue(new ExceptionCounterValue());
assertFalse(value.isComputed());
try {
value.compute();
}
catch (Exception ignored) {
}
assertTrue(value.isComputed());
}
public void testFunctionComputesOnce() throws Exception {
CounterFunction counter = new CounterFunction();
MemoizedFunctionToNotNull<String, String> f = m.createMemoizedFunction(counter, STRONG);
doTestComputesOnce(apply(f, "ok"), "ok1", counter);
}
public void testNullableFunctionComputesOnce() throws Exception {
CounterFunction counter = new CounterFunction();
MemoizedFunctionToNullable<String, String> f = m.createMemoizedFunctionWithNullableValues(counter, STRONG);
doTestComputesOnce(apply(f, "ok"), "ok1", counter);
}
public void testNullIsNotConfusedForNotComputedInFunction() throws Exception {
CounterFunctionToNull counter = new CounterFunctionToNull();
MemoizedFunctionToNullable<String, String> f = m.createMemoizedFunctionWithNullableValues(counter, STRONG);
doTestComputesOnce(apply(f, ""), null, counter);
}
public void testFunctionPreservesExceptions() throws Exception {
ExceptionCounterFunction counter = new ExceptionCounterFunction();
MemoizedFunctionToNotNull<String, String> f = m.createMemoizedFunction(counter, STRONG);
doTestExceptionPreserved(apply(f, ""), UnsupportedOperationException.class, counter);
}
public void testNullableFunctionPreservesExceptions() throws Exception {
ExceptionCounterFunction counter = new ExceptionCounterFunction();
MemoizedFunctionToNullable<String, String> f = m.createMemoizedFunctionWithNullableValues(counter, STRONG);
doTestExceptionPreserved(apply(f, ""), UnsupportedOperationException.class, counter);
}
// Values
public void testNotNullLazyComputedOnce() throws Exception {
CounterValue counter = new CounterValue();
NotNullLazyValue<String> value = m.createLazyValue(counter);
doTestComputesOnce(value, "ok1", counter);
}
public void testNullableLazyComputedOnce() throws Exception {
CounterValue counter = new CounterValue();
NullableLazyValue<String> value = m.createNullableLazyValue(counter);
doTestComputesOnce(value, "ok1", counter);
}
public void testNullIsNotConfusedForNotComputed() throws Exception {
CounterValueNull counter = new CounterValueNull();
NullableLazyValue<String> value = m.createNullableLazyValue(counter);
doTestComputesOnce(value, null, counter);
}
public void testNotNullLazyPreservesException() throws Exception {
ExceptionCounterValue counter = new ExceptionCounterValue();
NotNullLazyValue<String> value = m.createLazyValue(counter);
doTestExceptionPreserved(value, UnsupportedOperationException.class, counter);
}
public void testNullableLazyPreservesException() throws Exception {
ExceptionCounterValue counter = new ExceptionCounterValue();
NullableLazyValue<String> value = m.createNullableLazyValue(counter);
doTestExceptionPreserved(value, UnsupportedOperationException.class, counter);
}
public void testRecursionIntolerance() throws Exception {
class C {
NotNullLazyValue<String> rec = m.createLazyValue(new Computable<String>() {
@Override
public String compute() {
return rec.compute();
}
});
}
try {
new C().rec.compute();
fail();
}
catch (IllegalStateException e) {
// OK
}
}
public void testNullableRecursionIntolerance() throws Exception {
class C {
NullableLazyValue<String> rec = m.createNullableLazyValue(new Computable<String>() {
@Override
public String compute() {
return rec.compute();
}
});
}
try {
new C().rec.compute();
fail();
}
catch (IllegalStateException e) {
// OK
}
}
public void testRecursionTolerance() throws Exception {
class C {
NotNullLazyValue<String> rec = m.createRecursionTolerantLazyValue(new Computable<String>() {
@Override
public String compute() {
assertEquals("rec", rec.compute());
return "tolerant!";
}
}, "rec");
}
assertEquals("tolerant!", new C().rec.compute());
}
public void testNullableRecursionTolerance() throws Exception {
class C {
NullableLazyValue<String> rec = m.createRecursionTolerantNullableLazyValue(new Computable<String>() {
@Override
public String compute() {
assertEquals(null, rec.compute());
return "tolerant!";
}
}, null);
}
assertEquals("tolerant!", new C().rec.compute());
}
public void testRecursionIntoleranceWithPostCompute() throws Exception {
@SuppressWarnings("unchecked")
class C {
NotNullLazyValue<String> rec = m.createLazyValueWithPostCompute(
new Computable<String>() {
@Override
public String compute() {
return rec.compute();
}
},
null,
Consumer.EMPTY_CONSUMER
);
}
try {
new C().rec.compute();
fail();
}
catch (IllegalStateException e) {
// OK
}
}
public void testRecursionToleranceAndPostCompute() throws Exception {
final CounterImpl counter = new CounterImpl();
class C {
NotNullLazyValue<String> rec = m.createLazyValueWithPostCompute(
new Computable<String>() {
@Override
public String compute() {
return rec.compute();
}
},
new Function<Boolean, Object>() {
@Override
public Object fun(Boolean aBoolean) {
return "tolerant";
}
},
new Consumer<String>() {
@Override
public void consume(String s) {
counter.inc();
assertEquals("tolerant", s);
}
}
);
}
C c = new C();
assertEquals("tolerant", c.rec.compute());
c.rec.compute();
assertEquals("postCompute() called more than once", 1, counter.getCount());
}
public void testPostComputeNoRecursion() throws Exception {
final CounterImpl counter = new CounterImpl();
NotNullLazyValue<Collection<String>> v = m.createLazyValueWithPostCompute(
new Computable<Collection<String>>() {
@Override
public Collection<String> compute() {
List<String> strings = new ArrayList<String>();
strings.add("first");
return strings;
}
},
null,
new Consumer<Collection<String>>() {
@Override
public void consume(Collection<String> strings) {
counter.inc();
strings.add("postComputed");
}
}
);
assertEquals(Arrays.asList("first", "postComputed"), v.compute());
v.compute();
assertEquals(1, counter.getCount());
}
public void testNullablePostComputeNoRecursion() throws Exception {
final CounterImpl counter = new CounterImpl();
NullableLazyValue<Collection<String>> v = m.createNullableLazyValueWithPostCompute(
new Computable<Collection<String>>() {
@Override
public Collection<String> compute() {
ArrayList<String> strings = new ArrayList<String>();
strings.add("first");
return strings;
}
},
new Consumer<Collection<String>>() {
@Override
public void consume(Collection<String> strings) {
counter.inc();
strings.add("postComputed");
}
}
);
assertEquals(Arrays.asList("first", "postComputed"), v.compute());
v.compute();
assertEquals(1, counter.getCount());
}
public void testRecursionPreventionWithDefaultOnSecondRun() throws Exception {
@SuppressWarnings("unchecked")
class C {
NotNullLazyValue<String> rec = m.createLazyValueWithPostCompute(
new Computable<String>() {
@Override
public String compute() {
return rec.compute();
}
},
new Function<Boolean, Object>() {
@Override
public Object fun(Boolean firstTime) {
if (firstTime) {
return WrappedValues.escapeThrowable(new ReenteringLazyValueComputationException());
}
return "second";
}
},
new Consumer<String>() {
@Override
public void consume(String s) {
fail("Recursion-tolerating value should not be post computed");
}
}
);
}
C c = new C();
try {
c.rec.compute();
fail();
}
catch (ReenteringLazyValueComputationException e) {
// OK
}
assertEquals("second", c.rec.compute());
}
// Utilities
private static <K, V> Computable<V> apply(final Function<K, V> f, final K x) {
return new Computable<V>() {
@Override
public V compute() {
return f.fun(x);
}
};
}
private interface Counter {
int getCount();
}
private static class CounterImpl implements Counter {
private int count;
public void inc() {
count++;
}
@Override
public int getCount() {
return count;
}
}
private static class CounterValueNull extends CounterImpl implements Computable<String>, Counter {
@Override
public String compute() {
inc();
return null;
}
}
private static class CounterValue extends CounterValueNull {
@Override
public String compute() {
inc();
return "ok" + getCount();
}
}
private static class ExceptionCounterValue extends CounterValueNull {
@Override
public String compute() {
inc();
throw new UnsupportedOperationException();
}
}
private static class CounterFunctionToNull extends CounterImpl implements Function<String, String>, Counter {
@Override
public String fun(String s) {
inc();
return null;
}
}
private static class CounterFunction extends CounterFunctionToNull {
@Override
public String fun(String s) {
inc();
return s + getCount();
}
}
private static class ExceptionCounterFunction extends CounterFunctionToNull {
@Override
public String fun(String s) {
inc();
throw new UnsupportedOperationException();
}
}
}
@@ -206,7 +206,10 @@ public class LockBasedStorageManager implements StorageManager {
return typedValue;
}
catch (Throwable throwable) {
value = WrappedValues.escapeThrowable(throwable);
if (value == NotValue.COMPUTING) {
// Store only if it's a genuine result, not something thrown through recursionDetected()
value = WrappedValues.escapeThrowable(throwable);
}
throw ExceptionUtils.rethrow(throwable);
}
}