blob: 645eda01529f15137c048474b07850a136e3371a [file] [log] [blame]
/*
* Copyright (C) 2011 The Guava Authors
*
* 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.google.common.testing;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.testing.GcFinalization.FinalizationPredicate;
import com.google.common.util.concurrent.SettableFuture;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import junit.framework.TestCase;
/**
* Tests for {@link GcFinalization}.
*
* @author Martin Buchholz
* @author mike nonemacher
*/
public class GcFinalizationTest extends TestCase {
// ----------------------------------------------------------------
// Ordinary tests of successful method execution
// ----------------------------------------------------------------
public void testAwait_CountDownLatch() {
final CountDownLatch latch = new CountDownLatch(1);
Object x =
new Object() {
@Override
protected void finalize() {
latch.countDown();
}
};
x = null; // Hint to the JIT that x is unreachable
GcFinalization.await(latch);
assertEquals(0, latch.getCount());
}
public void testAwaitDone_Future() {
final SettableFuture<Void> future = SettableFuture.create();
Object x =
new Object() {
@Override
protected void finalize() {
future.set(null);
}
};
x = null; // Hint to the JIT that x is unreachable
GcFinalization.awaitDone(future);
assertTrue(future.isDone());
assertFalse(future.isCancelled());
}
public void testAwaitDone_Future_Cancel() {
final SettableFuture<Void> future = SettableFuture.create();
Object x =
new Object() {
@Override
protected void finalize() {
future.cancel(false);
}
};
x = null; // Hint to the JIT that x is unreachable
GcFinalization.awaitDone(future);
assertTrue(future.isDone());
assertTrue(future.isCancelled());
}
public void testAwaitClear() {
final WeakReference<Object> ref = new WeakReference<>(new Object());
GcFinalization.awaitClear(ref);
assertNull(ref.get());
}
public void testAwaitDone_FinalizationPredicate() {
final WeakHashMap<Object, Object> map = new WeakHashMap<>();
map.put(new Object(), Boolean.TRUE);
GcFinalization.awaitDone(
new FinalizationPredicate() {
@Override
public boolean isDone() {
return map.isEmpty();
}
});
assertTrue(map.isEmpty());
}
// ----------------------------------------------------------------
// Test that interrupts result in RuntimeException, not InterruptedException.
// Trickier than it looks, because runFinalization swallows interrupts.
// ----------------------------------------------------------------
class Interruptenator extends Thread {
final AtomicBoolean shutdown;
Interruptenator(final Thread interruptee) {
this(interruptee, new AtomicBoolean(false));
}
Interruptenator(final Thread interruptee, final AtomicBoolean shutdown) {
super(
new Runnable() {
@Override
public void run() {
while (!shutdown.get()) {
interruptee.interrupt();
Thread.yield();
}
}
});
this.shutdown = shutdown;
start();
}
void shutdown() {
shutdown.set(true);
while (this.isAlive()) {
Thread.yield();
}
}
}
void assertWrapsInterruptedException(RuntimeException e) {
assertThat(e).hasMessageThat().contains("Unexpected interrupt");
assertThat(e).hasCauseThat().isInstanceOf(InterruptedException.class);
}
public void testAwait_CountDownLatch_Interrupted() {
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
try {
final CountDownLatch latch = new CountDownLatch(1);
try {
GcFinalization.await(latch);
fail("should throw");
} catch (RuntimeException expected) {
assertWrapsInterruptedException(expected);
}
} finally {
interruptenator.shutdown();
Thread.interrupted();
}
}
public void testAwaitDone_Future_Interrupted_Interrupted() {
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
try {
final SettableFuture<Void> future = SettableFuture.create();
try {
GcFinalization.awaitDone(future);
fail("should throw");
} catch (RuntimeException expected) {
assertWrapsInterruptedException(expected);
}
} finally {
interruptenator.shutdown();
Thread.interrupted();
}
}
public void testAwaitClear_Interrupted() {
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
try {
final WeakReference<Object> ref = new WeakReference<Object>(Boolean.TRUE);
try {
GcFinalization.awaitClear(ref);
fail("should throw");
} catch (RuntimeException expected) {
assertWrapsInterruptedException(expected);
}
} finally {
interruptenator.shutdown();
Thread.interrupted();
}
}
public void testAwaitDone_FinalizationPredicate_Interrupted() {
Interruptenator interruptenator = new Interruptenator(Thread.currentThread());
try {
try {
GcFinalization.awaitDone(
new FinalizationPredicate() {
@Override
public boolean isDone() {
return false;
}
});
fail("should throw");
} catch (RuntimeException expected) {
assertWrapsInterruptedException(expected);
}
} finally {
interruptenator.shutdown();
Thread.interrupted();
}
}
/**
* awaitFullGc() is not quite as reliable a way to ensure calling of a specific finalize method as
* the more direct await* methods, but should be reliable enough in practice to avoid flakiness of
* this test. (And if it isn't, we'd like to know about it first!)
*/
public void testAwaitFullGc() {
final CountDownLatch finalizerRan = new CountDownLatch(1);
final WeakReference<Object> ref =
new WeakReference<Object>(
new Object() {
@Override
protected void finalize() {
finalizerRan.countDown();
}
});
// Don't copy this into your own test!
// Use e.g. awaitClear or await(CountDownLatch) instead.
GcFinalization.awaitFullGc();
// If this test turns out to be flaky, add a second call to awaitFullGc()
// GcFinalization.awaitFullGc();
assertEquals(0, finalizerRan.getCount());
assertNull(ref.get());
}
}