8042931: Implement classfile tests for EnclosingMethod attribute

Reviewed-by: jjg, shurailine, anazarov
This commit is contained in:
Andrei Eremeev 2014-11-12 15:16:35 +02:00
parent ed9c1bb743
commit 9130b22f3f
8 changed files with 631 additions and 109 deletions

View file

@ -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() {
};
}
}

View file

@ -74,13 +74,13 @@ public class DeprecatedPackageTest extends TestResult {
addTestCase(src); addTestCase(src);
printf("Testing test case: \n%s\n", src); printf("Testing test case: \n%s\n", src);
try { try {
ClassFile cf = ClassFile.read(compile( ClassFile cf = readClassFile(compile(
new String[]{"package-info.java", package_info}, new String[]{"package-info.java", package_info},
new String[]{"notDeprecated.java", src}) new String[]{"notDeprecated.java", src})
.getClasses().get(CLASS_NAME).openInputStream()); .getClasses().get(CLASS_NAME));
Deprecated_attribute attr = Deprecated_attribute attr =
(Deprecated_attribute) cf.getAttribute(Attribute.Deprecated); (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) { } catch (Exception e) {
addFailure(e); addFailure(e);
} }

View file

@ -240,7 +240,7 @@ public class DeprecatedTest extends TestResult {
? "deprecated" ? "deprecated"
: "notDeprecated"; : "notDeprecated";
echo("Testing outer class : " + outerClassName); echo("Testing outer class : " + outerClassName);
ClassFile cf = ClassFile.read(classes.get(outerClassName).openInputStream()); ClassFile cf = readClassFile(classes.get(outerClassName));
Deprecated_attribute attr = (Deprecated_attribute) Deprecated_attribute attr = (Deprecated_attribute)
cf.getAttribute(Attribute.Deprecated); cf.getAttribute(Attribute.Deprecated);
testAttribute(outerClassName, attr, cf); testAttribute(outerClassName, attr, cf);
@ -260,7 +260,7 @@ public class DeprecatedTest extends TestResult {
String innerClassName = cf.constant_pool. String innerClassName = cf.constant_pool.
getClassInfo(innerClass.inner_class_info_index).getName(); getClassInfo(innerClass.inner_class_info_index).getName();
echo("Testing inner class : " + innerClassName); echo("Testing inner class : " + innerClassName);
ClassFile innerCf = ClassFile.read(classes.get(innerClassName).openInputStream()); ClassFile innerCf = readClassFile(classes.get(innerClassName));
Deprecated_attribute attr = (Deprecated_attribute) Deprecated_attribute attr = (Deprecated_attribute)
innerCf.getAttribute(Attribute.Deprecated); innerCf.getAttribute(Attribute.Deprecated);
String innerClassSimpleName = innerClass.getInnerName(cf.constant_pool); String innerClassSimpleName = innerClass.getInnerName(cf.constant_pool);
@ -298,17 +298,18 @@ public class DeprecatedTest extends TestResult {
if (name.contains("deprecated")) { if (name.contains("deprecated")) {
testDeprecatedAttribute(name, attr, cf); testDeprecatedAttribute(name, attr, cf);
} else { } 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) private void testDeprecatedAttribute(String name, Deprecated_attribute attr, ClassFile cf)
throws ConstantPoolException { throws ConstantPoolException {
assertNotNull(attr, name + " must have deprecated attribute"); if (checkNotNull(attr, name + " must have deprecated attribute")) {
assertEquals(0, attr.attribute_length, checkEquals(0, attr.attribute_length,
"attribute_length should equal to 0"); "attribute_length should equal to 0");
assertEquals("Deprecated", checkEquals("Deprecated",
cf.constant_pool.getUTF8Value(attr.attribute_name_index), cf.constant_pool.getUTF8Value(attr.attribute_name_index),
name + " attribute_name_index"); name + " attribute_name_index");
} }
} }
}

View file

@ -96,21 +96,21 @@ public class InnerClassesHierarchyTest extends TestResult {
ClassFile cf = readClassFile(currentClassName); ClassFile cf = readClassFile(currentClassName);
InnerClasses_attribute attr = (InnerClasses_attribute) InnerClasses_attribute attr = (InnerClasses_attribute)
cf.getAttribute(Attribute.InnerClasses); cf.getAttribute(Attribute.InnerClasses);
assertNotNull(attr, "Class should not contain " checkNotNull(attr, "Class should not contain "
+ "inner classes attribute : " + currentClassName); + "inner classes attribute : " + currentClassName);
assertTrue(innerClasses.containsKey(currentClassName), checkTrue(innerClasses.containsKey(currentClassName),
"map contains class name : " + currentClassName); "map contains class name : " + currentClassName);
Set<String> setClasses = innerClasses.get(currentClassName); Set<String> setClasses = innerClasses.get(currentClassName);
if (setClasses == null) { if (setClasses == null) {
continue; continue;
} }
assertEquals(attr.number_of_classes, checkEquals(attr.number_of_classes,
setClasses.size(), setClasses.size(),
"Check number of inner classes : " + setClasses); "Check number of inner classes : " + setClasses);
for (Info info : attr.classes) { for (Info info : attr.classes) {
String innerClassName = info String innerClassName = info
.getInnerClassInfo(cf.constant_pool).getBaseName(); .getInnerClassInfo(cf.constant_pool).getBaseName();
assertTrue(setClasses.contains(innerClassName), checkTrue(setClasses.contains(innerClassName),
currentClassName + " contains inner class : " currentClassName + " contains inner class : "
+ innerClassName); + innerClassName);
if (visitedClasses.add(innerClassName)) { if (visitedClasses.add(innerClassName)) {
@ -124,7 +124,7 @@ public class InnerClassesHierarchyTest extends TestResult {
Set<String> a_b = removeAll(visitedClasses, allClasses); Set<String> a_b = removeAll(visitedClasses, allClasses);
Set<String> b_a = removeAll(allClasses, visitedClasses); Set<String> b_a = removeAll(allClasses, visitedClasses);
assertEquals(visitedClasses, allClasses, checkEquals(visitedClasses, allClasses,
"All classes are found\n" "All classes are found\n"
+ "visited - all classes : " + a_b + "visited - all classes : " + a_b
+ "\nall classes - visited : " + b_a); + "\nall classes - visited : " + b_a);

View file

@ -78,16 +78,16 @@ public class InnerClassesIndexTest extends TestResult {
continue; continue;
} }
foundClasses.add(innerName); foundClasses.add(innerName);
assertEquals(info.outer_class_info_index, 0, checkEquals(info.outer_class_info_index, 0,
"outer_class_info_index of " + innerName); "outer_class_info_index of " + innerName);
if (innerName.matches("\\$\\d+")) { if (innerName.matches("\\$\\d+")) {
assertEquals(info.inner_name_index, 0, checkEquals(info.inner_name_index, 0,
"inner_name_index of anonymous class"); "inner_name_index of anonymous class");
} }
} }
Set<String> expectedClasses = getInnerClasses(); Set<String> expectedClasses = getInnerClasses();
expectedClasses.remove("InnerClassesIndexTest$Inner"); expectedClasses.remove("InnerClassesIndexTest$Inner");
assertEquals(foundClasses, expectedClasses, "All classes are found"); checkEquals(foundClasses, expectedClasses, "All classes are found");
} catch (Exception e) { } catch (Exception e) {
addFailure(e); addFailure(e);
} finally { } finally {

View file

@ -202,44 +202,44 @@ public abstract class InnerClassesTestBase extends TestResult {
++count; ++count;
} }
} }
assertEquals(1, count, "Number of inner classes attribute"); checkEquals(1, count, "Number of inner classes attribute");
if (innerClasses == null) { if (!checkNotNull(innerClasses, "InnerClasses attribute should not be null")) {
return; return;
} }
assertEquals(cf.constant_pool. checkEquals(cf.constant_pool.
getUTF8Info(innerClasses.attribute_name_index).value, "InnerClasses", getUTF8Info(innerClasses.attribute_name_index).value, "InnerClasses",
"innerClasses.attribute_name_index"); "innerClasses.attribute_name_index");
// Inner Classes attribute consists of length (2 bytes) // Inner Classes attribute consists of length (2 bytes)
// and 8 bytes for each inner class's entry. // and 8 bytes for each inner class's entry.
assertEquals(innerClasses.attribute_length, checkEquals(innerClasses.attribute_length,
2 + 8 * class2Flags.size(), "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"); class2Flags.size(), "innerClasses.number_of_classes");
Set<String> visitedClasses = new HashSet<>(); Set<String> visitedClasses = new HashSet<>();
for (Info e : innerClasses.classes) { for (Info e : innerClasses.classes) {
String baseName = cf.constant_pool.getClassInfo( String baseName = cf.constant_pool.getClassInfo(
e.inner_class_info_index).getBaseName(); e.inner_class_info_index).getBaseName();
if (cf.major_version >= 51 && e.inner_name_index == 0) { if (cf.major_version >= 51 && e.inner_name_index == 0) {
assertEquals(e.outer_class_info_index, 0, checkEquals(e.outer_class_info_index, 0,
"outer_class_info_index " "outer_class_info_index "
+ "in case of inner_name_index is zero : " + "in case of inner_name_index is zero : "
+ baseName); + baseName);
} }
String className = baseName.replaceFirst(".*\\$", ""); String className = baseName.replaceFirst(".*\\$", "");
assertTrue(class2Flags.containsKey(className), checkTrue(class2Flags.containsKey(className),
className); className);
assertTrue(visitedClasses.add(className), checkTrue(visitedClasses.add(className),
"there are no duplicates in attribute : " + 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), class2Flags.get(className),
"inner_class_access_flags " + className); "inner_class_access_flags " + className);
if (!Arrays.asList(skipClasses).contains(className)) { if (!Arrays.asList(skipClasses).contains(className)) {
assertEquals( checkEquals(
cf.constant_pool.getClassInfo(e.inner_class_info_index).getBaseName(), cf.constant_pool.getClassInfo(e.inner_class_info_index).getBaseName(),
classToTest + "$" + className, classToTest + "$" + className,
"inner_class_info_index of " + className); "inner_class_info_index of " + className);
if (e.outer_class_info_index > 0) { if (e.outer_class_info_index > 0) {
assertEquals( checkEquals(
cf.constant_pool.getClassInfo(e.outer_class_info_index).getName(), cf.constant_pool.getClassInfo(e.outer_class_info_index).getName(),
classToTest, classToTest,
"outer_class_info_index of " + className); "outer_class_info_index of " + className);

View file

@ -25,10 +25,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays; import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -78,8 +75,8 @@ public class TestBase {
/** /**
* Compiles sources in memory. * Compiles sources in memory.
* *
* @param sources to compile. * @param sources to compile
* @return memory file manager which contains class files and class loader. * @return in-memory file manager which contains class files and class loader
*/ */
public InMemoryFileManager compile(String... sources) public InMemoryFileManager compile(String... sources)
throws IOException, CompilationException { throws IOException, CompilationException {
@ -91,7 +88,7 @@ public class TestBase {
* *
* @param options compiler options. * @param options compiler options.
* @param sources sources to compile. * @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) public InMemoryFileManager compile(List<String> options, String... sources)
throws IOException, CompilationException { throws IOException, CompilationException {
@ -102,7 +99,7 @@ public class TestBase {
* Compiles sources in memory. * Compiles sources in memory.
* *
* @param sources sources[i][0] - name of file, sources[i][1] - sources. * @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, public InMemoryFileManager compile(String[]... sources) throws IOException,
CompilationException { CompilationException {
@ -114,7 +111,7 @@ public class TestBase {
* *
* @param options compiler options * @param options compiler options
* @param sources sources[i][0] - name of file, sources[i][1] - sources. * @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) public InMemoryFileManager compile(List<String> options, String[]... sources)
throws IOException, CompilationException { throws IOException, CompilationException {
@ -142,7 +139,9 @@ public class TestBase {
* @throws ConstantPoolException if constant pool error occurs * @throws ConstantPoolException if constant pool error occurs
*/ */
public ClassFile readClassFile(JavaFileObject fileObject) throws IOException, ConstantPoolException { 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); 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() { public File getSourceDir() {
return new File(System.getProperty("test.src", ".")); return new File(System.getProperty("test.src", "."));
} }

View file

@ -24,10 +24,7 @@
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.*; import java.util.*;
import java.util.stream.Stream; import java.util.stream.Collectors;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
/** /**
* This class accumulates test results. Test results can be checked with method @{code checkStatus}. * 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() { private String errorMessage() {
return testCases.stream().filter(Info::isFailed) return testCases.stream().filter(Info::isFailed)
.map(tc -> format("Failure in test case:\n%s\n%s", tc.info(), tc.getMessage())) .map(tc -> String.format("Failure in test case:\n%s\n%s", tc.info(), tc.getMessage()))
.collect(joining("\n")); .collect(Collectors.joining("\n"));
} }
@Override public boolean checkEquals(Object actual, Object expected, String message) {
public void assertEquals(Object actual, Object expected, String message) { echo("Testing : " + message);
getLastTestCase().assertEquals(actual, expected, 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 boolean checkNull(Object actual, String message) {
public void assertNull(Object actual, String message) { return checkEquals(actual, null, message);
getLastTestCase().assertEquals(actual, null, message);
} }
@Override public boolean checkNotNull(Object actual, String message) {
public void assertNotNull(Object actual, String message) { echo("Testing : " + message);
getLastTestCase().assertNotNull(actual, message); if (Objects.isNull(actual)) {
getLastTestCase().addAssert(new AssertionFailedException(
message + " : Expected not null value"));
return false;
}
return true;
} }
@Override public boolean checkFalse(boolean actual, String message) {
public void assertFalse(boolean actual, String message) { return checkEquals(actual, false, message);
getLastTestCase().assertEquals(actual, false, message);
} }
@Override public boolean checkTrue(boolean actual, String message) {
public void assertTrue(boolean actual, String message) { return checkEquals(actual, true, message);
getLastTestCase().assertEquals(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); Set<?> copy = new HashSet<>(expected);
copy.removeAll(found); copy.removeAll(found);
assertTrue(found.containsAll(expected), message + " : " + copy); return checkTrue(found.containsAll(expected), message + " : " + copy);
} }
public void addFailure(Throwable th) { 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. * 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 * or an exception occurs
*/ */
public void checkStatus() throws TestFailedException { public void checkStatus() throws TestFailedException {
@ -115,7 +119,7 @@ public class TestResult extends TestBase {
private class Info { private class Info {
private final String info; private final String info;
private final List<String> asserts; private final List<AssertionFailedException> asserts;
private final List<Throwable> errors; private final List<Throwable> errors;
private Info(String info) { private Info(String info) {
@ -137,45 +141,20 @@ public class TestResult extends TestBase {
printf("[ERROR] : %s\n", getStackTrace(th)); printf("[ERROR] : %s\n", getStackTrace(th));
} }
public void addFailure(String message) { public void addAssert(AssertionFailedException e) {
String stackTrace = Stream.of(Thread.currentThread().getStackTrace()) asserts.add(e);
// just to get stack trace without TestResult and Thread printf("[ASSERT] : %s\n", getStackTrace(e));
.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 String getMessage() { public String getMessage() {
return (asserts.size() > 0 ? getAssertMessage() + "\n" : "") + getErrorMessage(); return (asserts.size() > 0 ? getErrorMessage("[ASSERT]", asserts) + "\n" : "")
+ getErrorMessage("[ERROR]", errors);
} }
public String getAssertMessage() { public String getErrorMessage(String header, List<? extends Throwable> list) {
return asserts.stream() return list.stream()
.map(failure -> "[ASSERT] : " + failure) .map(throwable -> String.format("%s : %s", header, getStackTrace(throwable)))
.collect(joining("\n")); .collect(Collectors.joining("\n"));
}
public String getErrorMessage() {
return errors.stream()
.map(throwable -> format("[ERROR] : %s", getStackTrace(throwable)))
.collect(joining("\n"));
} }
public String getStackTrace(Throwable throwable) { public String getStackTrace(Throwable throwable) {