mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8042931: Implement classfile tests for EnclosingMethod attribute
Reviewed-by: jjg, shurailine, anazarov
This commit is contained in:
parent
ed9c1bb743
commit
9130b22f3f
8 changed files with 631 additions and 109 deletions
|
@ -0,0 +1,537 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8042931
|
||||
* @summary Checking EnclosingMethod attribute of anonymous/local class.
|
||||
* @library /tools/lib /tools/javac/lib ../lib
|
||||
* @build EnclosingMethodTest TestBase TestResult InMemoryFileManager ToolBox
|
||||
* @run main EnclosingMethodTest
|
||||
*/
|
||||
|
||||
import com.sun.tools.classfile.Attribute;
|
||||
import com.sun.tools.classfile.ClassFile;
|
||||
import com.sun.tools.classfile.EnclosingMethod_attribute;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The test checks the enclosing method attribute of anonymous/local classes.
|
||||
* The top-level class contains the anonymous and local classes to be tested. The test examines
|
||||
* each inner class and determine whether the class should have the EnclosingMethod attribute or not.
|
||||
* Golden information about enclosing methods are held in annotation {@code ExpectedEnclosingMethod}.
|
||||
*
|
||||
* The test assumes that a class must have the EnclosingMethod attribute if the class is annotated or
|
||||
* if its parent class is annotated in case of anonymous class. In addition, classes
|
||||
* named {@code VariableInitializer} are introduced to test variable initializer cases. These classes
|
||||
* must not have the enclosing method attribute, but its anonymous derived class must.
|
||||
* After classification of classes, the test checks whether classes contain the correct enclosing
|
||||
* method attribute in case of anonymous/local class, or checks whether classes do not contain
|
||||
* the EnclosingMethod attribute, otherwise.
|
||||
*
|
||||
* Test cases:
|
||||
* top-level class as enclosing class:
|
||||
* 1. anonymous and local classes in static initializer;
|
||||
* 2. anonymous and local classes in instance initializer;
|
||||
* 3. anonymous and local classes in lambda;
|
||||
* 4. anonymous and local classes in constructor;
|
||||
* 5. anonymous and local classes in method;
|
||||
* 6. static and instance variable initializer.
|
||||
*
|
||||
* inner class as enclosing class:
|
||||
* 1. anonymous and local classes in static initializer;
|
||||
* 2. anonymous and local classes in instance initializer;
|
||||
* 3. anonymous and local classes in lambda;
|
||||
* 4. anonymous and local classes in constructor;
|
||||
* 5. anonymous and local classes in method;
|
||||
* 6. static and instance variable initializer.
|
||||
*
|
||||
* enum as enclosing class:
|
||||
* 1. anonymous and local classes in static initializer;
|
||||
* 2. anonymous and local classes in instance initializer;
|
||||
* 3. anonymous and local classes in lambda;
|
||||
* 4. anonymous and local classes in constructor;
|
||||
* 5. anonymous and local classes in method;
|
||||
* 6. static and instance variable initializer.
|
||||
*
|
||||
* interface as enclosing class:
|
||||
* 1. anonymous and local classes in lambda;
|
||||
* 2. anonymous and local classes in static method;
|
||||
* 3. anonymous and local classes in default method;
|
||||
* 4. static variable initializer.
|
||||
*
|
||||
* annotation as enclosing class:
|
||||
* 1. anonymous and local classes in lambda;
|
||||
* 2. static variable initializer.
|
||||
*/
|
||||
public class EnclosingMethodTest extends TestResult {
|
||||
|
||||
private final Map<Class<?>, ExpectedEnclosingMethod> class2EnclosingMethod = new HashMap<>();
|
||||
private final Set<Class<?>> noEnclosingMethod = new HashSet<>();
|
||||
|
||||
public EnclosingMethodTest() throws ClassNotFoundException {
|
||||
Class<EnclosingMethodTest> outerClass = EnclosingMethodTest.class;
|
||||
String outerClassName = outerClass.getSimpleName();
|
||||
File testClasses = getClassDir();
|
||||
FilenameFilter filter = (dir, name) -> name.matches(outerClassName + ".*\\.class");
|
||||
|
||||
for (File file : testClasses.listFiles(filter)) {
|
||||
Class<?> clazz = Class.forName(file.getName().replace(".class", ""));
|
||||
if (clazz.isAnonymousClass()) {
|
||||
// anonymous class cannot be annotated, information is in its parent class.
|
||||
ExpectedEnclosingMethod declaredAnnotation =
|
||||
clazz.getSuperclass().getDeclaredAnnotation(ExpectedEnclosingMethod.class);
|
||||
class2EnclosingMethod.put(clazz, declaredAnnotation);
|
||||
} else {
|
||||
ExpectedEnclosingMethod enclosingMethod = clazz.getDeclaredAnnotation(ExpectedEnclosingMethod.class);
|
||||
// if class is annotated and it does not contain information for variable initializer cases,
|
||||
// then it must have the enclosing method attribute.
|
||||
if (enclosingMethod != null && !clazz.getSimpleName().contains("VariableInitializer")) {
|
||||
class2EnclosingMethod.put(clazz, enclosingMethod);
|
||||
} else {
|
||||
noEnclosingMethod.add(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void test() throws TestFailedException {
|
||||
try {
|
||||
testEnclosingMethodAttribute();
|
||||
testLackOfEnclosingMethodAttribute();
|
||||
} finally {
|
||||
checkStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private void testLackOfEnclosingMethodAttribute() {
|
||||
for (Class<?> clazz : noEnclosingMethod) {
|
||||
try {
|
||||
addTestCase("Class should not have EnclosingMethod attribute : " + clazz);
|
||||
ClassFile classFile = readClassFile(clazz);
|
||||
checkEquals(countEnclosingMethodAttributes(classFile),
|
||||
0l, "number of the EnclosingMethod attribute in the class is zero : "
|
||||
+ classFile.getName());
|
||||
} catch (Exception e) {
|
||||
addFailure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testEnclosingMethodAttribute() {
|
||||
class2EnclosingMethod.forEach((clazz, enclosingMethod) -> {
|
||||
try {
|
||||
String info = enclosingMethod.info() + " "
|
||||
+ (clazz.isAnonymousClass() ? "anonymous" : "local");
|
||||
addTestCase(info);
|
||||
printf("Testing test case : %s\n", info);
|
||||
ClassFile classFile = readClassFile(clazz);
|
||||
String className = clazz.getName();
|
||||
checkEquals(countEnclosingMethodAttributes(classFile), 1l,
|
||||
"number of the EnclosingMethod attribute in the class is one : "
|
||||
+ clazz);
|
||||
EnclosingMethod_attribute attr = (EnclosingMethod_attribute)
|
||||
classFile.getAttribute(Attribute.EnclosingMethod);
|
||||
|
||||
if (!checkNotNull(attr, "the EnclosingMethod attribute is not null : " + className)) {
|
||||
// stop checking, attr is null. test case failed
|
||||
return;
|
||||
}
|
||||
checkEquals(classFile.constant_pool.getUTF8Value(attr.attribute_name_index),
|
||||
"EnclosingMethod",
|
||||
"attribute_name_index of EnclosingMethod attribute in the class : " + className);
|
||||
checkEquals(attr.attribute_length, 4,
|
||||
"attribute_length of EnclosingMethod attribute in the class : " + className);
|
||||
String expectedClassName = enclosingMethod.enclosingClazz().getName();
|
||||
checkEquals(classFile.constant_pool.getClassInfo(attr.class_index).getName(),
|
||||
expectedClassName, String.format(
|
||||
"enclosing class of EnclosingMethod attribute in the class %s is %s",
|
||||
className, expectedClassName));
|
||||
|
||||
String expectedMethodName = enclosingMethod.enclosingMethod();
|
||||
if (expectedMethodName.isEmpty()) {
|
||||
// class does not have an enclosing method
|
||||
checkEquals(attr.method_index, 0, String.format(
|
||||
"enclosing method of EnclosingMethod attribute in the class %s is null", className));
|
||||
} else {
|
||||
String methodName = classFile.constant_pool.getNameAndTypeInfo(attr.method_index).getName();
|
||||
checkTrue(methodName.startsWith(expectedMethodName), String.format(
|
||||
"enclosing method of EnclosingMethod attribute in the class %s" +
|
||||
" is method name %s" +
|
||||
", actual method name is %s",
|
||||
className, expectedMethodName, methodName));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
addFailure(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private long countEnclosingMethodAttributes(ClassFile classFile) {
|
||||
return Stream.of(classFile.attributes.attrs)
|
||||
.filter(x -> x instanceof EnclosingMethod_attribute)
|
||||
.count();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ExpectedEnclosingMethod {
|
||||
String info();
|
||||
Class<?> enclosingClazz();
|
||||
String enclosingMethod() default "";
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws ClassNotFoundException, TestFailedException {
|
||||
new EnclosingMethodTest().test();
|
||||
}
|
||||
|
||||
// Test cases: enclosing class is a top-level class
|
||||
static {
|
||||
// anonymous and local classes in static initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingStaticInitialization in EnclosingMethodTest",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
class EnclosingStaticInitialization {
|
||||
}
|
||||
new EnclosingStaticInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
// anonymous and local classes in instance initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingInitialization in EnclosingMethodTest",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
class EnclosingInitialization {
|
||||
}
|
||||
new EnclosingInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
Runnable lambda = () -> {
|
||||
// anonymous and local classes in lambda
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingLambda in EnclosingMethodTest",
|
||||
enclosingMethod = "lambda",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
class EnclosingLambda {
|
||||
}
|
||||
new EnclosingLambda() {
|
||||
};
|
||||
};
|
||||
|
||||
EnclosingMethodTest(int i) {
|
||||
// anonymous and local classes in constructor
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingConstructor in EnclosingMethodTest",
|
||||
enclosingMethod = "<init>",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
class EnclosingConstructor {
|
||||
}
|
||||
new EnclosingConstructor() {
|
||||
};
|
||||
}
|
||||
|
||||
void method() {
|
||||
// anonymous and local classes in method
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingMethod in EnclosingMethodTest",
|
||||
enclosingMethod = "method",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
class EnclosingMethod {
|
||||
}
|
||||
new EnclosingMethod() {
|
||||
};
|
||||
}
|
||||
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "VariableInitializer in EnclosingMethodTest",
|
||||
enclosingClazz = EnclosingMethodTest.class
|
||||
)
|
||||
static class VariableInitializer {
|
||||
}
|
||||
|
||||
// static variable initializer
|
||||
private static final VariableInitializer cvi = new VariableInitializer() {
|
||||
};
|
||||
|
||||
// instance variable initializer
|
||||
private final VariableInitializer ivi = new VariableInitializer() {
|
||||
};
|
||||
|
||||
// Test cases: enclosing class is an inner class
|
||||
public static class notEnclosing01 {
|
||||
static {
|
||||
// anonymous and local classes in static initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingStaticInitialization in notEnclosing01",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
class EnclosingStaticInitialization {
|
||||
}
|
||||
new EnclosingStaticInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
// anonymous and local classes in instance initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingInitialization in notEnclosing01",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
class EnclosingInitialization {
|
||||
}
|
||||
new EnclosingInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
Runnable lambda = () -> {
|
||||
// anonymous and local classes in lambda
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingLambda in notEnclosing01",
|
||||
enclosingMethod = "lambda",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
class EnclosingLambda {
|
||||
}
|
||||
new EnclosingLambda() {
|
||||
};
|
||||
};
|
||||
|
||||
notEnclosing01() {
|
||||
// anonymous and local classes in constructor
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingConstructor in notEnclosing01",
|
||||
enclosingMethod = "<init>",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
class EnclosingConstructor {
|
||||
}
|
||||
new EnclosingConstructor() {
|
||||
};
|
||||
}
|
||||
|
||||
void method() {
|
||||
// anonymous and local classes in method
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingMethod in notEnclosing01",
|
||||
enclosingMethod = "method",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
class EnclosingMethod {
|
||||
}
|
||||
new EnclosingMethod() {
|
||||
};
|
||||
}
|
||||
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "VariableInitializer in notEnclosing01",
|
||||
enclosingClazz = notEnclosing01.class
|
||||
)
|
||||
static class VariableInitializer {
|
||||
}
|
||||
|
||||
// static variable initializer
|
||||
private static final VariableInitializer cvi = new VariableInitializer() {
|
||||
};
|
||||
|
||||
// instance variable initializer
|
||||
private final VariableInitializer ivi = new VariableInitializer() {
|
||||
};
|
||||
}
|
||||
|
||||
// Test cases: enclosing class is an interface
|
||||
public interface notEnclosing02 {
|
||||
Runnable lambda = () -> {
|
||||
// anonymous and local classes in lambda
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingLambda in notEnclosing02",
|
||||
enclosingMethod = "lambda",
|
||||
enclosingClazz = notEnclosing02.class
|
||||
)
|
||||
class EnclosingLambda {
|
||||
}
|
||||
new EnclosingLambda() {
|
||||
};
|
||||
};
|
||||
|
||||
static void staticMethod() {
|
||||
// anonymous and local classes in static method
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingMethod in notEnclosing02",
|
||||
enclosingMethod = "staticMethod",
|
||||
enclosingClazz = notEnclosing02.class
|
||||
)
|
||||
class EnclosingMethod {
|
||||
}
|
||||
new EnclosingMethod() {
|
||||
};
|
||||
}
|
||||
|
||||
default void defaultMethod() {
|
||||
// anonymous and local classes in default method
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingMethod in notEnclosing02",
|
||||
enclosingMethod = "defaultMethod",
|
||||
enclosingClazz = notEnclosing02.class
|
||||
)
|
||||
class EnclosingMethod {
|
||||
}
|
||||
new EnclosingMethod() {
|
||||
};
|
||||
}
|
||||
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "VariableInitializer in notEnclosing02",
|
||||
enclosingClazz = notEnclosing02.class
|
||||
)
|
||||
static class VariableInitializer {
|
||||
}
|
||||
|
||||
// static variable initializer
|
||||
VariableInitializer cvi = new VariableInitializer() {
|
||||
};
|
||||
}
|
||||
|
||||
// Test cases: enclosing class is an enum
|
||||
public enum notEnclosing03 {;
|
||||
|
||||
static {
|
||||
// anonymous and local classes in static initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingStaticInitialization in notEnclosing03",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
class EnclosingStaticInitialization {
|
||||
}
|
||||
new EnclosingStaticInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
// anonymous and local classes in instance initializer
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingInitialization in notEnclosing03",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
class EnclosingInitialization {
|
||||
}
|
||||
new EnclosingInitialization() {
|
||||
};
|
||||
}
|
||||
|
||||
Runnable lambda = () -> {
|
||||
// anonymous and local classes in lambda
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingLambda in notEnclosing03",
|
||||
enclosingMethod = "lambda",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
class EnclosingLambda {
|
||||
}
|
||||
new EnclosingLambda() {
|
||||
};
|
||||
};
|
||||
|
||||
notEnclosing03() {
|
||||
// anonymous and local classes in constructor
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingConstructor in notEnclosing03",
|
||||
enclosingMethod = "<init>",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
class EnclosingConstructor {
|
||||
}
|
||||
new EnclosingConstructor() {
|
||||
};
|
||||
}
|
||||
|
||||
void method() {
|
||||
// anonymous and local classes in method
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingMethod in notEnclosing03",
|
||||
enclosingMethod = "method",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
class EnclosingMethod {
|
||||
}
|
||||
new EnclosingMethod() {
|
||||
};
|
||||
}
|
||||
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "VariableInitializer in notEnclosing03",
|
||||
enclosingClazz = notEnclosing03.class
|
||||
)
|
||||
static class VariableInitializer {
|
||||
}
|
||||
|
||||
// static variable initializer
|
||||
private static final VariableInitializer cvi = new VariableInitializer() {
|
||||
};
|
||||
|
||||
// instance variable initializer
|
||||
private final VariableInitializer ivi = new VariableInitializer() {
|
||||
};
|
||||
}
|
||||
|
||||
// Test cases: enclosing class is an annotation
|
||||
public @interface notEnclosing04 {
|
||||
Runnable lambda = () -> {
|
||||
// anonymous and local classes in lambda
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "EnclosingLambda in notEnclosing04",
|
||||
enclosingMethod = "lambda",
|
||||
enclosingClazz = notEnclosing04.class
|
||||
)
|
||||
class EnclosingLambda {
|
||||
}
|
||||
new EnclosingLambda() {
|
||||
};
|
||||
};
|
||||
|
||||
@ExpectedEnclosingMethod(
|
||||
info = "VariableInitializer in notEnclosing04",
|
||||
enclosingClazz = notEnclosing04.class
|
||||
)
|
||||
static class VariableInitializer {
|
||||
}
|
||||
|
||||
// static variable initializer
|
||||
VariableInitializer cvi = new VariableInitializer() {
|
||||
};
|
||||
}
|
||||
}
|
|
@ -74,13 +74,13 @@ public class DeprecatedPackageTest extends TestResult {
|
|||
addTestCase(src);
|
||||
printf("Testing test case: \n%s\n", src);
|
||||
try {
|
||||
ClassFile cf = ClassFile.read(compile(
|
||||
ClassFile cf = readClassFile(compile(
|
||||
new String[]{"package-info.java", package_info},
|
||||
new String[]{"notDeprecated.java", src})
|
||||
.getClasses().get(CLASS_NAME).openInputStream());
|
||||
.getClasses().get(CLASS_NAME));
|
||||
Deprecated_attribute attr =
|
||||
(Deprecated_attribute) cf.getAttribute(Attribute.Deprecated);
|
||||
assertNull(attr, "Class can not have deprecated attribute : " + CLASS_NAME);
|
||||
checkNull(attr, "Class can not have deprecated attribute : " + CLASS_NAME);
|
||||
} catch (Exception e) {
|
||||
addFailure(e);
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ public class DeprecatedTest extends TestResult {
|
|||
? "deprecated"
|
||||
: "notDeprecated";
|
||||
echo("Testing outer class : " + outerClassName);
|
||||
ClassFile cf = ClassFile.read(classes.get(outerClassName).openInputStream());
|
||||
ClassFile cf = readClassFile(classes.get(outerClassName));
|
||||
Deprecated_attribute attr = (Deprecated_attribute)
|
||||
cf.getAttribute(Attribute.Deprecated);
|
||||
testAttribute(outerClassName, attr, cf);
|
||||
|
@ -260,7 +260,7 @@ public class DeprecatedTest extends TestResult {
|
|||
String innerClassName = cf.constant_pool.
|
||||
getClassInfo(innerClass.inner_class_info_index).getName();
|
||||
echo("Testing inner class : " + innerClassName);
|
||||
ClassFile innerCf = ClassFile.read(classes.get(innerClassName).openInputStream());
|
||||
ClassFile innerCf = readClassFile(classes.get(innerClassName));
|
||||
Deprecated_attribute attr = (Deprecated_attribute)
|
||||
innerCf.getAttribute(Attribute.Deprecated);
|
||||
String innerClassSimpleName = innerClass.getInnerName(cf.constant_pool);
|
||||
|
@ -298,17 +298,18 @@ public class DeprecatedTest extends TestResult {
|
|||
if (name.contains("deprecated")) {
|
||||
testDeprecatedAttribute(name, attr, cf);
|
||||
} else {
|
||||
assertNull(attr, name + " should not have deprecated attribute");
|
||||
checkNull(attr, name + " should not have deprecated attribute");
|
||||
}
|
||||
}
|
||||
|
||||
private void testDeprecatedAttribute(String name, Deprecated_attribute attr, ClassFile cf)
|
||||
throws ConstantPoolException {
|
||||
assertNotNull(attr, name + " must have deprecated attribute");
|
||||
assertEquals(0, attr.attribute_length,
|
||||
"attribute_length should equal to 0");
|
||||
assertEquals("Deprecated",
|
||||
cf.constant_pool.getUTF8Value(attr.attribute_name_index),
|
||||
name + " attribute_name_index");
|
||||
if (checkNotNull(attr, name + " must have deprecated attribute")) {
|
||||
checkEquals(0, attr.attribute_length,
|
||||
"attribute_length should equal to 0");
|
||||
checkEquals("Deprecated",
|
||||
cf.constant_pool.getUTF8Value(attr.attribute_name_index),
|
||||
name + " attribute_name_index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,21 +96,21 @@ public class InnerClassesHierarchyTest extends TestResult {
|
|||
ClassFile cf = readClassFile(currentClassName);
|
||||
InnerClasses_attribute attr = (InnerClasses_attribute)
|
||||
cf.getAttribute(Attribute.InnerClasses);
|
||||
assertNotNull(attr, "Class should not contain "
|
||||
checkNotNull(attr, "Class should not contain "
|
||||
+ "inner classes attribute : " + currentClassName);
|
||||
assertTrue(innerClasses.containsKey(currentClassName),
|
||||
checkTrue(innerClasses.containsKey(currentClassName),
|
||||
"map contains class name : " + currentClassName);
|
||||
Set<String> setClasses = innerClasses.get(currentClassName);
|
||||
if (setClasses == null) {
|
||||
continue;
|
||||
}
|
||||
assertEquals(attr.number_of_classes,
|
||||
checkEquals(attr.number_of_classes,
|
||||
setClasses.size(),
|
||||
"Check number of inner classes : " + setClasses);
|
||||
for (Info info : attr.classes) {
|
||||
String innerClassName = info
|
||||
.getInnerClassInfo(cf.constant_pool).getBaseName();
|
||||
assertTrue(setClasses.contains(innerClassName),
|
||||
checkTrue(setClasses.contains(innerClassName),
|
||||
currentClassName + " contains inner class : "
|
||||
+ innerClassName);
|
||||
if (visitedClasses.add(innerClassName)) {
|
||||
|
@ -124,10 +124,10 @@ public class InnerClassesHierarchyTest extends TestResult {
|
|||
|
||||
Set<String> a_b = removeAll(visitedClasses, allClasses);
|
||||
Set<String> b_a = removeAll(allClasses, visitedClasses);
|
||||
assertEquals(visitedClasses, allClasses,
|
||||
checkEquals(visitedClasses, allClasses,
|
||||
"All classes are found\n"
|
||||
+ "visited - all classes : " + a_b
|
||||
+ "\nall classes - visited : " + b_a);
|
||||
+ "visited - all classes : " + a_b
|
||||
+ "\nall classes - visited : " + b_a);
|
||||
} catch (Exception e) {
|
||||
addFailure(e);
|
||||
} finally {
|
||||
|
|
|
@ -78,16 +78,16 @@ public class InnerClassesIndexTest extends TestResult {
|
|||
continue;
|
||||
}
|
||||
foundClasses.add(innerName);
|
||||
assertEquals(info.outer_class_info_index, 0,
|
||||
checkEquals(info.outer_class_info_index, 0,
|
||||
"outer_class_info_index of " + innerName);
|
||||
if (innerName.matches("\\$\\d+")) {
|
||||
assertEquals(info.inner_name_index, 0,
|
||||
checkEquals(info.inner_name_index, 0,
|
||||
"inner_name_index of anonymous class");
|
||||
}
|
||||
}
|
||||
Set<String> expectedClasses = getInnerClasses();
|
||||
expectedClasses.remove("InnerClassesIndexTest$Inner");
|
||||
assertEquals(foundClasses, expectedClasses, "All classes are found");
|
||||
checkEquals(foundClasses, expectedClasses, "All classes are found");
|
||||
} catch (Exception e) {
|
||||
addFailure(e);
|
||||
} finally {
|
||||
|
|
|
@ -202,44 +202,44 @@ public abstract class InnerClassesTestBase extends TestResult {
|
|||
++count;
|
||||
}
|
||||
}
|
||||
assertEquals(1, count, "Number of inner classes attribute");
|
||||
if (innerClasses == null) {
|
||||
checkEquals(1, count, "Number of inner classes attribute");
|
||||
if (!checkNotNull(innerClasses, "InnerClasses attribute should not be null")) {
|
||||
return;
|
||||
}
|
||||
assertEquals(cf.constant_pool.
|
||||
checkEquals(cf.constant_pool.
|
||||
getUTF8Info(innerClasses.attribute_name_index).value, "InnerClasses",
|
||||
"innerClasses.attribute_name_index");
|
||||
// Inner Classes attribute consists of length (2 bytes)
|
||||
// and 8 bytes for each inner class's entry.
|
||||
assertEquals(innerClasses.attribute_length,
|
||||
checkEquals(innerClasses.attribute_length,
|
||||
2 + 8 * class2Flags.size(), "innerClasses.attribute_length");
|
||||
assertEquals(innerClasses.number_of_classes,
|
||||
checkEquals(innerClasses.number_of_classes,
|
||||
class2Flags.size(), "innerClasses.number_of_classes");
|
||||
Set<String> visitedClasses = new HashSet<>();
|
||||
for (Info e : innerClasses.classes) {
|
||||
String baseName = cf.constant_pool.getClassInfo(
|
||||
e.inner_class_info_index).getBaseName();
|
||||
if (cf.major_version >= 51 && e.inner_name_index == 0) {
|
||||
assertEquals(e.outer_class_info_index, 0,
|
||||
"outer_class_info_index "
|
||||
+ "in case of inner_name_index is zero : "
|
||||
+ baseName);
|
||||
checkEquals(e.outer_class_info_index, 0,
|
||||
"outer_class_info_index "
|
||||
+ "in case of inner_name_index is zero : "
|
||||
+ baseName);
|
||||
}
|
||||
String className = baseName.replaceFirst(".*\\$", "");
|
||||
assertTrue(class2Flags.containsKey(className),
|
||||
checkTrue(class2Flags.containsKey(className),
|
||||
className);
|
||||
assertTrue(visitedClasses.add(className),
|
||||
checkTrue(visitedClasses.add(className),
|
||||
"there are no duplicates in attribute : " + className);
|
||||
assertEquals(e.inner_class_access_flags.getInnerClassFlags(),
|
||||
checkEquals(e.inner_class_access_flags.getInnerClassFlags(),
|
||||
class2Flags.get(className),
|
||||
"inner_class_access_flags " + className);
|
||||
if (!Arrays.asList(skipClasses).contains(className)) {
|
||||
assertEquals(
|
||||
cf.constant_pool.getClassInfo(e.inner_class_info_index).getBaseName(),
|
||||
classToTest + "$" + className,
|
||||
"inner_class_info_index of " + className);
|
||||
checkEquals(
|
||||
cf.constant_pool.getClassInfo(e.inner_class_info_index).getBaseName(),
|
||||
classToTest + "$" + className,
|
||||
"inner_class_info_index of " + className);
|
||||
if (e.outer_class_info_index > 0) {
|
||||
assertEquals(
|
||||
checkEquals(
|
||||
cf.constant_pool.getClassInfo(e.outer_class_info_index).getName(),
|
||||
classToTest,
|
||||
"outer_class_info_index of " + className);
|
||||
|
|
|
@ -25,10 +25,7 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -78,8 +75,8 @@ public class TestBase {
|
|||
/**
|
||||
* Compiles sources in memory.
|
||||
*
|
||||
* @param sources to compile.
|
||||
* @return memory file manager which contains class files and class loader.
|
||||
* @param sources to compile
|
||||
* @return in-memory file manager which contains class files and class loader
|
||||
*/
|
||||
public InMemoryFileManager compile(String... sources)
|
||||
throws IOException, CompilationException {
|
||||
|
@ -91,7 +88,7 @@ public class TestBase {
|
|||
*
|
||||
* @param options compiler options.
|
||||
* @param sources sources to compile.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
* @return in-memory file manager which contains class files and class loader.
|
||||
*/
|
||||
public InMemoryFileManager compile(List<String> options, String... sources)
|
||||
throws IOException, CompilationException {
|
||||
|
@ -102,7 +99,7 @@ public class TestBase {
|
|||
* Compiles sources in memory.
|
||||
*
|
||||
* @param sources sources[i][0] - name of file, sources[i][1] - sources.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
* @return in-memory file manager which contains class files and class loader.
|
||||
*/
|
||||
public InMemoryFileManager compile(String[]... sources) throws IOException,
|
||||
CompilationException {
|
||||
|
@ -114,7 +111,7 @@ public class TestBase {
|
|||
*
|
||||
* @param options compiler options
|
||||
* @param sources sources[i][0] - name of file, sources[i][1] - sources.
|
||||
* @return map where key is className, value is corresponding ClassFile.
|
||||
* @return in-memory file manager which contains class files and class loader.
|
||||
*/
|
||||
public InMemoryFileManager compile(List<String> options, String[]... sources)
|
||||
throws IOException, CompilationException {
|
||||
|
@ -142,7 +139,9 @@ public class TestBase {
|
|||
* @throws ConstantPoolException if constant pool error occurs
|
||||
*/
|
||||
public ClassFile readClassFile(JavaFileObject fileObject) throws IOException, ConstantPoolException {
|
||||
return readClassFile(fileObject.openInputStream());
|
||||
try (InputStream is = fileObject.openInputStream()) {
|
||||
return readClassFile(is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +204,12 @@ public class TestBase {
|
|||
assertEquals(actual, false, message);
|
||||
}
|
||||
|
||||
public void assertContains(Set<?> found, Set<?> expected, String message) {
|
||||
Set<?> copy = new HashSet<>(expected);
|
||||
copy.removeAll(found);
|
||||
assertTrue(found.containsAll(expected), message + " : " + copy);
|
||||
}
|
||||
|
||||
public File getSourceDir() {
|
||||
return new File(System.getProperty("test.src", "."));
|
||||
}
|
||||
|
|
|
@ -24,10 +24,7 @@
|
|||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This class accumulates test results. Test results can be checked with method @{code checkStatus}.
|
||||
|
@ -52,39 +49,46 @@ public class TestResult extends TestBase {
|
|||
|
||||
private String errorMessage() {
|
||||
return testCases.stream().filter(Info::isFailed)
|
||||
.map(tc -> format("Failure in test case:\n%s\n%s", tc.info(), tc.getMessage()))
|
||||
.collect(joining("\n"));
|
||||
.map(tc -> String.format("Failure in test case:\n%s\n%s", tc.info(), tc.getMessage()))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertEquals(Object actual, Object expected, String message) {
|
||||
getLastTestCase().assertEquals(actual, expected, message);
|
||||
public boolean checkEquals(Object actual, Object expected, String message) {
|
||||
echo("Testing : " + message);
|
||||
if (!Objects.equals(actual, expected)) {
|
||||
getLastTestCase().addAssert(new AssertionFailedException(
|
||||
String.format("%s%nGot: %s, Expected: %s", message, actual, expected)));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertNull(Object actual, String message) {
|
||||
getLastTestCase().assertEquals(actual, null, message);
|
||||
public boolean checkNull(Object actual, String message) {
|
||||
return checkEquals(actual, null, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertNotNull(Object actual, String message) {
|
||||
getLastTestCase().assertNotNull(actual, message);
|
||||
public boolean checkNotNull(Object actual, String message) {
|
||||
echo("Testing : " + message);
|
||||
if (Objects.isNull(actual)) {
|
||||
getLastTestCase().addAssert(new AssertionFailedException(
|
||||
message + " : Expected not null value"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertFalse(boolean actual, String message) {
|
||||
getLastTestCase().assertEquals(actual, false, message);
|
||||
public boolean checkFalse(boolean actual, String message) {
|
||||
return checkEquals(actual, false, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertTrue(boolean actual, String message) {
|
||||
getLastTestCase().assertEquals(actual, true, message);
|
||||
public boolean checkTrue(boolean actual, String message) {
|
||||
return checkEquals(actual, true, message);
|
||||
}
|
||||
|
||||
public void assertContains(Set<?> found, Set<?> expected, String message) {
|
||||
public boolean checkContains(Set<?> found, Set<?> expected, String message) {
|
||||
Set<?> copy = new HashSet<>(expected);
|
||||
copy.removeAll(found);
|
||||
assertTrue(found.containsAll(expected), message + " : " + copy);
|
||||
return checkTrue(found.containsAll(expected), message + " : " + copy);
|
||||
}
|
||||
|
||||
public void addFailure(Throwable th) {
|
||||
|
@ -99,10 +103,10 @@ public class TestResult extends TestBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Throws {@code TestFailedException} if one of the asserts are failed
|
||||
* Throws {@code TestFailedException} if one of the checks are failed
|
||||
* or an exception occurs. Prints error message of failed test cases.
|
||||
*
|
||||
* @throws TestFailedException if one of the asserts are failed
|
||||
* @throws TestFailedException if one of the checks are failed
|
||||
* or an exception occurs
|
||||
*/
|
||||
public void checkStatus() throws TestFailedException {
|
||||
|
@ -115,7 +119,7 @@ public class TestResult extends TestBase {
|
|||
private class Info {
|
||||
|
||||
private final String info;
|
||||
private final List<String> asserts;
|
||||
private final List<AssertionFailedException> asserts;
|
||||
private final List<Throwable> errors;
|
||||
|
||||
private Info(String info) {
|
||||
|
@ -137,45 +141,20 @@ public class TestResult extends TestBase {
|
|||
printf("[ERROR] : %s\n", getStackTrace(th));
|
||||
}
|
||||
|
||||
public void addFailure(String message) {
|
||||
String stackTrace = Stream.of(Thread.currentThread().getStackTrace())
|
||||
// just to get stack trace without TestResult and Thread
|
||||
.filter(e -> !"TestResult.java".equals(e.getFileName()) &&
|
||||
!"java.lang.Thread".equals(e.getClassName()))
|
||||
.map(e -> "\tat " + e)
|
||||
.collect(joining("\n"));
|
||||
asserts.add(format("%s\n%s", message, stackTrace));
|
||||
printf("[ASSERT] : %s\n", message);
|
||||
}
|
||||
|
||||
public void assertEquals(Object actual, Object expected, String message) {
|
||||
echo("Testing : " + message);
|
||||
if (!Objects.equals(actual, expected)) {
|
||||
addFailure(message + ": Got: " + actual + ", " + "Expected: " + expected);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertNotNull(Object actual, String message) {
|
||||
echo("Testing : " + message);
|
||||
if (actual == null) {
|
||||
addFailure(message + " : Expected not null value");
|
||||
}
|
||||
public void addAssert(AssertionFailedException e) {
|
||||
asserts.add(e);
|
||||
printf("[ASSERT] : %s\n", getStackTrace(e));
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return (asserts.size() > 0 ? getAssertMessage() + "\n" : "") + getErrorMessage();
|
||||
return (asserts.size() > 0 ? getErrorMessage("[ASSERT]", asserts) + "\n" : "")
|
||||
+ getErrorMessage("[ERROR]", errors);
|
||||
}
|
||||
|
||||
public String getAssertMessage() {
|
||||
return asserts.stream()
|
||||
.map(failure -> "[ASSERT] : " + failure)
|
||||
.collect(joining("\n"));
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errors.stream()
|
||||
.map(throwable -> format("[ERROR] : %s", getStackTrace(throwable)))
|
||||
.collect(joining("\n"));
|
||||
public String getErrorMessage(String header, List<? extends Throwable> list) {
|
||||
return list.stream()
|
||||
.map(throwable -> String.format("%s : %s", header, getStackTrace(throwable)))
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public String getStackTrace(Throwable throwable) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue