blob: f2e5523936c7777d884e1a1418398b1e655a2fd6 [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* 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.intellij.openapi.util;
import junit.framework.TestCase;
/**
* @author peter
*/
public class RecursionManagerTest extends TestCase {
private final RecursionGuard myGuard = RecursionManager.createGuard("RecursionManagerTest");
def prevent(Object key, boolean memoize = true, Closure c) {
myGuard.doPreventingRecursion(key, memoize, c as Computable)
}
public void testPreventRecursion() {
assert "foo-return" == prevent(["foo"]) {
assert "bar-return" == prevent("bar") {
assert null == prevent(["foo"]) { "foo-return" }
return "bar-return"
}
return "foo-return"
}
}
public void testMemoization() {
assert "foo-return" == prevent("foo") {
assert "bar-return" == prevent("bar") {
assert null == prevent("foo") { "foo-return" }
return "bar-return"
}
assert "bar-return" == prevent("bar") {
fail()
}
return "foo-return"
}
}
public void testNoMemoizationAfterExit() {
assert "foo-return" == prevent("foo") {
assert "bar-return" == prevent("bar") {
assert null == prevent("foo") { "foo-return" }
return "bar-return"
}
return "foo-return"
}
assert "bar-return2" == prevent("bar") {
return "bar-return2"
}
}
public void testMayCache() {
def doo1 = myGuard.markStack()
assert "doo-return" == prevent("doo") {
def foo1 = myGuard.markStack()
assert "foo-return" == prevent("foo") {
def bar1 = myGuard.markStack()
assert "bar-return" == prevent("bar") {
def foo2 = myGuard.markStack()
assert null == prevent("foo") { "foo-return" }
assert !foo2.mayCacheNow()
return "bar-return"
}
assert !bar1.mayCacheNow()
def goo1 = myGuard.markStack()
assert "goo-return" == prevent("goo") {
return "goo-return"
}
assert goo1.mayCacheNow()
assert !bar1.mayCacheNow()
return "foo-return"
}
assert foo1.mayCacheNow()
return "doo-return"
}
assert doo1.mayCacheNow()
}
public void testNoCachingForMemoizedValues() {
assert "foo-return" == prevent("foo") {
assert "bar-return" == prevent("bar") {
assert null == prevent("foo") { "foo-return" }
return "bar-return"
}
def stamp = myGuard.markStack()
assert "bar-return" == prevent("bar") {
fail()
}
assert !stamp.mayCacheNow()
return "foo-return"
}
}
public void testNoCachingForMemoizedValues2() {
assert "1-return" == prevent("1") {
assert "2-return" == prevent("2") {
assert "3-return" == prevent("3") {
assert null == prevent("2") { "2-return" }
assert null == prevent("1") { "1-return" }
return "3-return"
}
return "2-return"
}
def stamp = myGuard.markStack()
assert "2-return" == prevent("2") { fail() }
assert !stamp.mayCacheNow()
stamp = myGuard.markStack()
assert "3-return" == prevent("3") { fail() }
assert !stamp.mayCacheNow()
return "1-return"
}
}
public void testNoMemoizationForNoReentrancy() {
assert "foo-return" == prevent("foo") {
assert null == prevent("foo") { "foo-return" }
assert "bar-return" == prevent("bar") { "bar-return" }
def stamp = myGuard.markStack()
assert "bar-return2" == prevent("bar") { "bar-return2" }
assert stamp.mayCacheNow()
return "foo-return"
}
}
public void testFullGraphPerformance() throws Exception {
long start = System.currentTimeMillis()
int count = 20
Closure cl
cl = {
for (i in 1..count) {
prevent("foo" + i, cl)
}
return "zoo"
}
assert "zoo" == cl()
assert System.currentTimeMillis() - start < 10000
}
public void "test changing hash code doesn't crash RecursionManager"() {
def key = ["b"]
prevent(key) {
key << "a"
}
}
public void "test exception from hashCode on exiting"() {
def key1 = new ThrowingKey()
def key2 = new ThrowingKey()
def key3 = new ThrowingKey()
prevent(key1) {
prevent(key2) {
prevent(key3) {
key1.fail = key2.fail = key3.fail = true
}
}
}
}
private static class ThrowingKey {
boolean fail = false
@Override
int hashCode() {
if (fail) {
throw new RuntimeException()
}
return 0
}
@Override
boolean equals(Object obj) {
if (fail) {
throw new RuntimeException()
}
return super.equals(obj)
}
}
}