diff --git a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java index 3f71c8dfe548..3131b160ccc8 100644 --- a/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java +++ b/android/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java @@ -45,6 +45,10 @@ import com.google.common.collect.Range; import com.google.common.primitives.Ints; import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -87,6 +91,34 @@ public void testSuccess() throws ExecutionException, InterruptedException { .isEqualTo(value); } + @J2ktIncompatible + @AndroidIncompatible + @SuppressWarnings({ + "Java8ApiChecker", // We check isJava8 before Runtime and Runtime before using ClassModel + "deprecation", // Version.feature() isn't available until 10, so we use major() to support 9 + }) + public void testAssertNoClinit() throws Exception { + if (isJava8() || Runtime.version().major() < 24) { + return; + } + + String abstractFuturePath = AbstractFuture.class.getName().replace('.', '/') + ".class"; + + try (InputStream stream = + AbstractFuture.class.getClassLoader().getResourceAsStream(abstractFuturePath)) { + ClassModel classModel = ClassFile.of().parse(stream.readAllBytes()); + + for (MethodModel method : classModel.methods()) { + if (method.methodName().stringValue().equals("")) { + assertWithMessage( + "AbstractFuture should not have a static initializer () " + + "to prevent potential class-loading deadlocks.") + .fail(); + } + } + } + } + @J2ktIncompatible // J2KT ExecutionException differs in stack trace public void testException() throws InterruptedException { Throwable failure = new Throwable(); @@ -814,7 +846,6 @@ public void testSetFuture_stackOverflow() { // Verify that StackOverflowError in a long chain of SetFuture doesn't cause the entire toString // call to fail @J2ktIncompatible - @GwtIncompatible @AndroidIncompatible // b/391667564: crashes from stack overflows public void testSetFutureToString_stackOverflow() { SettableFuture orig = SettableFuture.create(); @@ -1290,4 +1321,8 @@ protected void interruptTask() { private static boolean isWindows() { return OS_NAME.value().startsWith("Windows"); } + + private static boolean isJava8() { + return JAVA_SPECIFICATION_VERSION.value().equals("1.8"); + } } diff --git a/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java b/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java index 3f71c8dfe548..3131b160ccc8 100644 --- a/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java +++ b/guava-tests/test/com/google/common/util/concurrent/AbstractFutureTest.java @@ -45,6 +45,10 @@ import com.google.common.collect.Range; import com.google.common.primitives.Ints; import com.google.common.util.concurrent.internal.InternalFutureFailureAccess; +import java.io.InputStream; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.classfile.MethodModel; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -87,6 +91,34 @@ public void testSuccess() throws ExecutionException, InterruptedException { .isEqualTo(value); } + @J2ktIncompatible + @AndroidIncompatible + @SuppressWarnings({ + "Java8ApiChecker", // We check isJava8 before Runtime and Runtime before using ClassModel + "deprecation", // Version.feature() isn't available until 10, so we use major() to support 9 + }) + public void testAssertNoClinit() throws Exception { + if (isJava8() || Runtime.version().major() < 24) { + return; + } + + String abstractFuturePath = AbstractFuture.class.getName().replace('.', '/') + ".class"; + + try (InputStream stream = + AbstractFuture.class.getClassLoader().getResourceAsStream(abstractFuturePath)) { + ClassModel classModel = ClassFile.of().parse(stream.readAllBytes()); + + for (MethodModel method : classModel.methods()) { + if (method.methodName().stringValue().equals("")) { + assertWithMessage( + "AbstractFuture should not have a static initializer () " + + "to prevent potential class-loading deadlocks.") + .fail(); + } + } + } + } + @J2ktIncompatible // J2KT ExecutionException differs in stack trace public void testException() throws InterruptedException { Throwable failure = new Throwable(); @@ -814,7 +846,6 @@ public void testSetFuture_stackOverflow() { // Verify that StackOverflowError in a long chain of SetFuture doesn't cause the entire toString // call to fail @J2ktIncompatible - @GwtIncompatible @AndroidIncompatible // b/391667564: crashes from stack overflows public void testSetFutureToString_stackOverflow() { SettableFuture orig = SettableFuture.create(); @@ -1290,4 +1321,8 @@ protected void interruptTask() { private static boolean isWindows() { return OS_NAME.value().startsWith("Windows"); } + + private static boolean isJava8() { + return JAVA_SPECIFICATION_VERSION.value().equals("1.8"); + } }