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);
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);
}

View file

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

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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", "."));
}

View file

@ -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) {