8186265: Make toString() methods of "task" objects more useful

Reviewed-by: martin, psandoz, rriggs, dholmes, darcy
This commit is contained in:
Charles Munger 2017-10-03 13:55:05 -07:00 committed by Doug Lea
parent 2ea646cc20
commit 229cce5f44
10 changed files with 240 additions and 23 deletions

@ -2490,13 +2490,13 @@ public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {
for (Completion p = stack; p != null; p = p.next)
++count;
return super.toString() +
((r == null) ?
((count == 0) ?
"[Not completed]" :
"[Not completed, " + count + " dependents]") :
(((r instanceof AltResult) && ((AltResult)r).ex != null) ?
"[Completed exceptionally]" :
"[Completed normally]"));
((r == null)
? ((count == 0)
? "[Not completed]"
: "[Not completed, " + count + " dependents]")
: (((r instanceof AltResult) && ((AltResult)r).ex != null)
? "[Completed exceptionally: " + ((AltResult)r).ex + "]"
: "[Completed normally]"));
}
// jdk9 additions

@ -514,6 +514,9 @@ public class Executors {
task.run();
return result;
}
public String toString() {
return super.toString() + "[Wrapped task = " + task + "]";
}
}
/**
@ -540,6 +543,10 @@ public class Executors {
throw e.getException();
}
}
public String toString() {
return super.toString() + "[Wrapped task = " + task + "]";
}
}
/**
@ -592,6 +599,10 @@ public class Executors {
throw e.getException();
}
}
public String toString() {
return super.toString() + "[Wrapped task = " + task + "]";
}
}
/**

@ -1375,6 +1375,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
public final void setRawResult(T v) { result = v; }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
public String toString() {
return super.toString() + "[Wrapped task = " + runnable + "]";
}
private static final long serialVersionUID = 5232453952276885070L;
}
@ -1392,6 +1395,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
public final void setRawResult(Void v) { }
public final boolean exec() { runnable.run(); return true; }
public final void run() { invoke(); }
public String toString() {
return super.toString() + "[Wrapped task = " + runnable + "]";
}
private static final long serialVersionUID = 5232453952276885070L;
}
@ -1437,6 +1443,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
}
}
public final void run() { invoke(); }
public String toString() {
return super.toString() + "[Wrapped task = " + callable + "]";
}
private static final long serialVersionUID = 2838392045355241008L;
}

@ -480,6 +480,41 @@ public class FutureTask<V> implements RunnableFuture<V> {
}
}
/**
* Returns a string representation of this FutureTask.
*
* @implSpec
* The default implementation returns a string identifying this
* FutureTask, as well as its completion state. The state, in
* brackets, contains one of the strings {@code "Completed Normally"},
* {@code "Completed Exceptionally"}, {@code "Cancelled"}, or {@code
* "Not completed"}.
*
* @return a string representation of this FutureTask
*/
public String toString() {
final String status;
switch (state) {
case NORMAL:
status = "[Completed normally]";
break;
case EXCEPTIONAL:
status = "[Completed exceptionally: " + outcome + "]";
break;
case CANCELLED:
case INTERRUPTING:
case INTERRUPTED:
status = "[Cancelled]";
break;
default:
final Callable<?> callable = this.callable;
status = (callable == null)
? "[Not completed]"
: "[Not completed, task = " + callable + "]";
}
return super.toString() + status;
}
// VarHandle mechanics
private static final VarHandle STATE;
private static final VarHandle RUNNER;

@ -74,7 +74,7 @@ public class Basic {
check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false");
check(!cf.isCancelled(), "Expected isCancelled to be false");
check(!cf.cancel(true), "Expected cancel to return false");
check(cf.toString().contains("[Completed normally]"));
check(cf.toString().matches(".*\\[.*Completed normally.*\\]"));
check(cf.complete(null) == false, "Expected complete() to fail");
check(cf.completeExceptionally(new Throwable()) == false,
"Expected completeExceptionally() to fail");
@ -106,7 +106,7 @@ public class Basic {
check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally");
check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled());
check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true));
check(cf.toString().contains("[Completed exceptionally]")); // ## TODO: 'E'xceptionally
check(cf.toString().matches(".*\\[.*Completed exceptionally.*\\]")); // ## TODO: 'E'xceptionally
check(cf.complete((T)new Object()) == false, "Expected complete() to fail");
check(cf.completeExceptionally(new Throwable()) == false,
"Expected completeExceptionally() to fail, already completed");

@ -86,7 +86,7 @@ public class CompletableFutureTest extends JSR166TestCase {
void checkIncomplete(CompletableFuture<?> f) {
assertFalse(f.isDone());
assertFalse(f.isCancelled());
assertTrue(f.toString().contains("Not completed"));
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
try {
assertNull(f.getNow(null));
} catch (Throwable fail) { threadUnexpectedException(fail); }
@ -109,7 +109,7 @@ public class CompletableFutureTest extends JSR166TestCase {
assertTrue(f.isDone());
assertFalse(f.isCancelled());
assertFalse(f.isCompletedExceptionally());
assertTrue(f.toString().contains("[Completed normally]"));
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
}
/**
@ -165,7 +165,7 @@ public class CompletableFutureTest extends JSR166TestCase {
assertFalse(f.isCancelled());
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
assertTrue(f.toString().contains("[Completed exceptionally]"));
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
}
void checkCompletedWithWrappedCFException(CompletableFuture<?> f) {
@ -220,7 +220,7 @@ public class CompletableFutureTest extends JSR166TestCase {
assertTrue(f.isDone());
assertTrue(f.isCompletedExceptionally());
assertTrue(f.isCancelled());
assertTrue(f.toString().contains("[Completed exceptionally]"));
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
}
/**
@ -356,23 +356,40 @@ public class CompletableFutureTest extends JSR166TestCase {
/**
* toString indicates current completion state
*/
public void testToString() {
CompletableFuture<String> f;
f = new CompletableFuture<String>();
assertTrue(f.toString().contains("[Not completed]"));
public void testToString_incomplete() {
CompletableFuture<String> f = new CompletableFuture<String>();
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
if (testImplementationDetails)
assertEquals(identityString(f) + "[Not completed]",
f.toString());
}
public void testToString_normal() {
CompletableFuture<String> f = new CompletableFuture<String>();
assertTrue(f.complete("foo"));
assertTrue(f.toString().contains("[Completed normally]"));
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
if (testImplementationDetails)
assertEquals(identityString(f) + "[Completed normally]",
f.toString());
}
f = new CompletableFuture<String>();
public void testToString_exception() {
CompletableFuture<String> f = new CompletableFuture<String>();
assertTrue(f.completeExceptionally(new IndexOutOfBoundsException()));
assertTrue(f.toString().contains("[Completed exceptionally]"));
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
if (testImplementationDetails)
assertTrue(f.toString().startsWith(
identityString(f) + "[Completed exceptionally: "));
}
public void testToString_cancelled() {
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
f = new CompletableFuture<String>();
CompletableFuture<String> f = new CompletableFuture<String>();
assertTrue(f.cancel(mayInterruptIfRunning));
assertTrue(f.toString().contains("[Completed exceptionally]"));
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
if (testImplementationDetails)
assertTrue(f.toString().startsWith(
identityString(f) + "[Completed exceptionally: "));
}
}

@ -613,4 +613,56 @@ public class ExecutorsTest extends JSR166TestCase {
} catch (NullPointerException success) {}
}
/**
* callable(runnable, x).toString() contains toString of wrapped task
*/
public void testCallable_withResult_toString() {
if (testImplementationDetails) {
Runnable r = () -> {};
Callable<String> c = Executors.callable(r, "");
assertEquals(
identityString(c) + "[Wrapped task = " + r.toString() + "]",
c.toString());
}
}
/**
* callable(runnable).toString() contains toString of wrapped task
*/
public void testCallable_toString() {
if (testImplementationDetails) {
Runnable r = () -> {};
Callable<Object> c = Executors.callable(r);
assertEquals(
identityString(c) + "[Wrapped task = " + r.toString() + "]",
c.toString());
}
}
/**
* privilegedCallable(callable).toString() contains toString of wrapped task
*/
public void testPrivilegedCallable_toString() {
if (testImplementationDetails) {
Callable<String> c = () -> "";
Callable<String> priv = Executors.privilegedCallable(c);
assertEquals(
identityString(priv) + "[Wrapped task = " + c.toString() + "]",
priv.toString());
}
}
/**
* privilegedCallableUsingCurrentClassLoader(callable).toString()
* contains toString of wrapped task
*/
public void testPrivilegedCallableUsingCurrentClassLoader_toString() {
if (testImplementationDetails) {
Callable<String> c = () -> "";
Callable<String> priv = Executors.privilegedCallableUsingCurrentClassLoader(c);
assertEquals(
identityString(priv) + "[Wrapped task = " + c.toString() + "]",
priv.toString());
}
}
}

@ -35,6 +35,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.Arrays;
import java.util.HashSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
@ -1675,4 +1676,42 @@ public class ForkJoinTaskTest extends JSR166TestCase {
testInvokeOnPool(mainPool(), a);
}
/**
* adapt(runnable).toString() contains toString of wrapped task
*/
public void testAdapt_Runnable_toString() {
if (testImplementationDetails) {
Runnable r = () -> {};
ForkJoinTask<?> task = ForkJoinTask.adapt(r);
assertEquals(
identityString(task) + "[Wrapped task = " + r.toString() + "]",
task.toString());
}
}
/**
* adapt(runnable, x).toString() contains toString of wrapped task
*/
public void testAdapt_Runnable_withResult_toString() {
if (testImplementationDetails) {
Runnable r = () -> {};
ForkJoinTask<String> task = ForkJoinTask.adapt(r, "");
assertEquals(
identityString(task) + "[Wrapped task = " + r.toString() + "]",
task.toString());
}
}
/**
* adapt(callable).toString() contains toString of wrapped task
*/
public void testAdapt_Callable_toString() {
if (testImplementationDetails) {
Callable<String> c = () -> "";
ForkJoinTask<String> task = ForkJoinTask.adapt(c);
assertEquals(
identityString(task) + "[Wrapped task = " + c.toString() + "]",
task.toString());
}
}
}

@ -861,4 +861,45 @@ public class FutureTaskTest extends JSR166TestCase {
}
}
/**
* toString indicates current completion state
*/
public void testToString_incomplete() {
FutureTask<String> f = new FutureTask<String>(() -> "");
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]"));
if (testImplementationDetails)
assertTrue(f.toString().startsWith(
identityString(f) + "[Not completed, task ="));
}
public void testToString_normal() {
FutureTask<String> f = new FutureTask<String>(() -> "");
f.run();
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]"));
if (testImplementationDetails)
assertEquals(identityString(f) + "[Completed normally]",
f.toString());
}
public void testToString_exception() {
FutureTask<String> f = new FutureTask<String>(
() -> { throw new ArithmeticException(); });
f.run();
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]"));
if (testImplementationDetails)
assertTrue(f.toString().startsWith(
identityString(f) + "[Completed exceptionally: "));
}
public void testToString_cancelled() {
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) {
FutureTask<String> f = new FutureTask<String>(() -> "");
assertTrue(f.cancel(mayInterruptIfRunning));
assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]"));
if (testImplementationDetails)
assertEquals(identityString(f) + "[Cancelled]",
f.toString());
}
}
}

@ -571,6 +571,7 @@ public class JSR166TestCase extends TestCase {
"DoubleAdderTest",
"ForkJoinPool8Test",
"ForkJoinTask8Test",
"HashMapTest",
"LinkedBlockingDeque8Test",
"LinkedBlockingQueue8Test",
"LongAccumulatorTest",
@ -1940,6 +1941,18 @@ public class JSR166TestCase extends TestCase {
Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
}
/**
* Returns the same String as would be returned by {@link
* Object#toString}, whether or not the given object's class
* overrides toString().
*
* @see System#identityHashCode
*/
static String identityString(Object x) {
return x.getClass().getName()
+ "@" + Integer.toHexString(System.identityHashCode(x));
}
// --- Shared assertions for Executor tests ---
/**