This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit fc0f18944b029502e1b1971aa47b3df39ab97911 Author: Eric Milles <[email protected]> AuthorDate: Thu Apr 24 14:58:45 2025 -0500 refactor test --- .../classgen/ClassCompletionVerifierTest.java | 233 +++++++++++---------- 1 file changed, 124 insertions(+), 109 deletions(-) diff --git a/src/test/groovy/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java b/src/test/groovy/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java index 157372a1b5..acd1652586 100644 --- a/src/test/groovy/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java +++ b/src/test/groovy/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java @@ -24,182 +24,199 @@ import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.control.SourceUnit; +import org.junit.Test; import java.io.PrintWriter; import java.io.StringWriter; -public class ClassCompletionVerifierTest extends TestSupport { - private SourceUnit source; - private ClassCompletionVerifier verifier; - private static final String ABSTRACT_FINAL_CLASS = "AbstractFinalClass"; - private static final String FINAL_INTERFACE = "FinalInterface"; - private static final String EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE = - "The class '" + ABSTRACT_FINAL_CLASS + "' must not be both final and abstract."; - private static final String EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE = - "The interface '" + FINAL_INTERFACE + "' must not be final. It is by definition abstract."; - private static final String EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object xxx()' from interface 'zzz' must not be final. It is by definition abstract."; - private static final String EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object yyy()' from interface 'zzz' must not be static. Only fields may be static in an interface."; - private static final String EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE = - "The class 'DodgyClass' has an incorrect modifier transient."; - // can't check synchronized here as it doubles up with ACC_SUPER - //private static final String EXPECTED_SYNCHRONIZED_CLASS_ERROR_MESSAGE = - // "The class 'DodgyClass' has an incorrect modifier synchronized."; - private static final String EXPECTED_NATIVE_CLASS_ERROR_MESSAGE = - "The class 'DodgyClass' has an incorrect modifier native."; - private static final String EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE = - "The class 'DodgyClass' has an incorrect modifier volatile."; - private static final String EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE = - "Repetitive method name/signature for method 'java.lang.Object xxx()' in class 'zzz'."; - private static final String EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE = - "Repetitive method name/signature for method 'java.lang.Object xxx(java.lang.String)' in interface 'zzz'."; - // can't check volatile here as it doubles up with bridge - //private static final String EXPECTED_VOLATILE_METHOD_ERROR_MESSAGE = - // "The method 'java.lang.Object vo()' has an incorrect modifier volatile."; - private static final String EXPECTED_STRICT_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object st()' has an incorrect modifier strictfp."; - private static final String EXPECTED_NATIVE_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object na()' has an incorrect modifier native."; - private static final String EXPECTED_SYNCHRONIZED_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object sy()' has an incorrect modifier synchronized."; - private static final String EXPECTED_TRANSIENT_METHOD_ERROR_MESSAGE = - "The method 'java.lang.Object tr()' has an incorrect modifier transient."; - private static final String EXPECTED_PROTECTED_FIELD_ERROR_MESSAGE = - "The field 'prof' is not 'public static final' but is defined in interface 'zzz'."; - private static final String EXPECTED_PRIVATE_FIELD_ERROR_MESSAGE = - "The field 'prif' is not 'public static final' but is defined in interface 'zzz'."; - private static final String EXPECTED_PROTECTED_METHOD_ERROR_MESSAGE = - "Method 'prom' is protected but should be public in interface 'zzz'."; - private static final String EXPECTED_PRIVATE_METHOD_ERROR_MESSAGE = - "Method 'prim' is private but should be public in interface 'zzz'."; - private static final String EXPECTED_ABSTRACT_PRIVATE_METHOD_ERROR_MESSAGE = - "Method 'y' from class 'X' must not be private as it is declared as an abstract method."; - - protected void setUp() throws Exception { - super.setUp(); - source = SourceUnit.create("dummy.groovy", ""); - verifier = new ClassCompletionVerifier(source); - } - - public void testDetectsAbstractPrivateMethod() throws Exception { +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.objectweb.asm.Opcodes.ACC_ABSTRACT; +import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.objectweb.asm.Opcodes.ACC_INTERFACE; +import static org.objectweb.asm.Opcodes.ACC_NATIVE; +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PROTECTED; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ACC_STATIC; +import static org.objectweb.asm.Opcodes.ACC_STRICT; +import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED; +import static org.objectweb.asm.Opcodes.ACC_TRANSIENT; +import static org.objectweb.asm.Opcodes.ACC_VOLATILE; + +public final class ClassCompletionVerifierTest { + + private SourceUnit source = SourceUnit.create("dummy.groovy", ""); + private ClassCompletionVerifier verifier = new ClassCompletionVerifier(source); + + @Test + public void shouldDetectAbstractPrivateMethod() { ClassNode node = new ClassNode("X", ACC_ABSTRACT, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("y", ACC_PRIVATE | ACC_ABSTRACT, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); + verifier.visitClass(node); - checkErrorMessage(EXPECTED_ABSTRACT_PRIVATE_METHOD_ERROR_MESSAGE); + + checkErrorMessage("Method 'y' from class 'X' must not be private as it is declared as an abstract method."); } - public void testDetectsFinalAbstractClass() throws Exception { + @Test + public void shouldDetectFinalAbstractClass() { checkVisitErrors("FinalClass", ACC_FINAL, false); checkVisitErrors("AbstractClass", ACC_ABSTRACT, false); - checkVisitErrors(ABSTRACT_FINAL_CLASS, ACC_ABSTRACT | ACC_FINAL, true); - checkErrorMessage(EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE); + checkVisitErrors("AbstractFinalClass", ACC_ABSTRACT | ACC_FINAL, true); + checkErrorMessage("The class 'AbstractFinalClass' must not be both final and abstract."); } - public void testDetectsDuplicateMethodsForClassNoParams() throws Exception { - checkDetectsDuplicateMethods(0, EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE, Parameter.EMPTY_ARRAY); + @Test + public void shouldDetectDuplicateMethodsForClassNoParams() { + tryDetectDuplicateMethods(ACC_ABSTRACT, "Repetitive method name/signature for method 'java.lang.Object xxx()' in class 'zzz'."); } - public void testDetectsDuplicateMethodsForInterfaceOneParam() throws Exception { - Parameter[] stringParam = {new Parameter(ClassHelper.STRING_TYPE, "x")}; - checkDetectsDuplicateMethods(ACC_INTERFACE, EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE, stringParam); + @Test + public void shouldDetectDuplicateMethodsForInterfaceOneParam() { + tryDetectDuplicateMethods(ACC_INTERFACE, "Repetitive method name/signature for method 'java.lang.Object xxx(java.lang.String)' in interface 'zzz'.", new Parameter(ClassHelper.STRING_TYPE, "x")); } - private void checkDetectsDuplicateMethods(int modifiers, String expectedErrorMessage, Parameter[] params) { + private void tryDetectDuplicateMethods(int modifiers, String expectedErrorMessage, Parameter... params) { ClassNode node = new ClassNode("zzz", modifiers, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("xxx", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("xxx", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY, null)); + verifier.visitClass(node); + checkErrorCount(2); checkErrorMessage(expectedErrorMessage); } - public void testDetectsIncorrectOtherModifier() throws Exception { + @Test + public void shouldDetectIncorrectOtherModifier() { // can't check synchronized here as it doubles up with ACC_SUPER checkVisitErrors("DodgyClass", ACC_TRANSIENT | ACC_VOLATILE | ACC_NATIVE, true); - checkErrorMessage(EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_NATIVE_CLASS_ERROR_MESSAGE); + checkErrorMessage("The class 'DodgyClass' has an incorrect modifier transient."); + checkErrorMessage("The class 'DodgyClass' has an incorrect modifier volatile."); + checkErrorMessage("The class 'DodgyClass' has an incorrect modifier native."); } - public void testDetectsFinalAbstractInterface() throws Exception { - checkVisitErrors(FINAL_INTERFACE, ACC_ABSTRACT | ACC_FINAL | ACC_INTERFACE, true); - checkErrorMessage(EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE); + @Test + public void shouldDetectFinalAbstractInterface() { + checkVisitErrors("FinalInterface", ACC_ABSTRACT | ACC_FINAL | ACC_INTERFACE, true); + checkErrorMessage("The interface 'FinalInterface' must not be final. It is by definition abstract."); } - public void testDetectsFinalAndStaticMethodsInInterface() throws Exception { + @Test + public void shouldDetectFinalMethodsInInterface() { ClassNode node = new ClassNode("zzz", ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("xxx", ACC_PUBLIC | ACC_FINAL, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("yyy", ACC_PUBLIC | ACC_STATIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); - addDummyConstructor(node); + addStaticConstructor(node); + verifier.visitClass(node); + checkErrorCount(2); - checkErrorMessage(EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE); + checkErrorMessage("The method 'java.lang.Object xxx()' from interface 'zzz' must not be final. It is by definition abstract."); + checkErrorMessage("The method 'java.lang.Object yyy()' from interface 'zzz' must not be static. Only fields may be static in an interface."); } - public void testDetectsIncorrectMethodModifiersInInterface() throws Exception { - // can't check volatile here as it doubles up with bridge + @Test + public void shouldDetectIncorrectMethodModifiersInInterface() { ClassNode node = new ClassNode("zzz", ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE); - node.addMethod(new MethodNode("st", ACC_STRICT, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("na", ACC_NATIVE, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); + node.addMethod(new MethodNode("st", ACC_STRICT, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("sy", ACC_SYNCHRONIZED, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("tr", ACC_TRANSIENT, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); - addDummyConstructor(node); + // can't check volatile here as it doubles up with bridge + addStaticConstructor(node); + verifier.visitClass(node); + checkErrorCount(4); - checkErrorMessage(EXPECTED_STRICT_METHOD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_NATIVE_METHOD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_SYNCHRONIZED_METHOD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_TRANSIENT_METHOD_ERROR_MESSAGE); + checkErrorMessage("The method 'java.lang.Object na()' has an incorrect modifier native."); + checkErrorMessage("The method 'java.lang.Object st()' has an incorrect modifier strictfp."); + checkErrorMessage("The method 'java.lang.Object sy()' has an incorrect modifier synchronized."); + checkErrorMessage("The method 'java.lang.Object tr()' has an incorrect modifier transient."); } - public void testDetectsIncorrectMemberVisibilityInInterface() throws Exception { + @Test + public void shouldDetectIncorrectMemberVisibilityInInterface() { ClassNode node = new ClassNode("zzz", ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("prim", ACC_PRIVATE, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("prom", ACC_PROTECTED, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addField("prif", ACC_PRIVATE, ClassHelper.OBJECT_TYPE, null); node.addField("prof", ACC_PROTECTED, ClassHelper.OBJECT_TYPE, null); - addDummyConstructor(node); + addStaticConstructor(node); + verifier.visitClass(node); + checkErrorCount(4); - checkErrorMessage(EXPECTED_PROTECTED_FIELD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_PRIVATE_FIELD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_PROTECTED_METHOD_ERROR_MESSAGE); - checkErrorMessage(EXPECTED_PRIVATE_METHOD_ERROR_MESSAGE); + checkErrorMessage("The field 'prof' is not 'public static final' but is defined in interface 'zzz'."); + checkErrorMessage("The field 'prif' is not 'public static final' but is defined in interface 'zzz'."); + checkErrorMessage("Method 'prom' is protected but should be public in interface 'zzz'."); + checkErrorMessage("Method 'prim' is private but should be public in interface 'zzz'."); } - public void testDetectsCorrectMethodModifiersInClass() throws Exception { + @Test + public void shouldAcceptCorrectMethodModifiersInClass() { // can't check volatile here as it doubles up with bridge ClassNode node = new ClassNode("zzz", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("st", ACC_STRICT, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("na", ACC_NATIVE, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); node.addMethod(new MethodNode("sy", ACC_SYNCHRONIZED, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); - addDummyConstructor(node); + addStaticConstructor(node); + verifier.visitClass(node); + checkErrorCount(0); } - public void testDetectsIncorrectMethodModifiersInClass() throws Exception { - // can't check volatile here as it doubles up with bridge + @Test + public void shouldDetectIncorrectMethodModifiersInClass() { ClassNode node = new ClassNode("zzz", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); node.addMethod(new MethodNode("tr", ACC_TRANSIENT, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); - addDummyConstructor(node); + // can't check volatile here as it doubles up with bridge + addStaticConstructor(node); + verifier.visitClass(node); + checkErrorCount(1); - checkErrorMessage(EXPECTED_TRANSIENT_METHOD_ERROR_MESSAGE); + checkErrorMessage("The method 'java.lang.Object tr()' has an incorrect modifier transient."); } - public void testDetectsInvalidFieldModifiers() { - ClassNode foo = new ClassNode("foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); - FieldNode bar = new FieldNode("bar", (ACC_FINAL | ACC_VOLATILE), ClassHelper.STRING_TYPE, foo, null); - foo.addField(bar); - verifier.visitClass(foo); + @Test + public void shouldDetectInvalidFieldModifiers() { + ClassNode node = new ClassNode("foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE); + node.addField(new FieldNode("bar", (ACC_FINAL | ACC_VOLATILE), ClassHelper.STRING_TYPE, node, null)); + + verifier.visitClass(node); + checkErrorCount(1); checkErrorMessage("Illegal combination of modifiers, final and volatile, for field 'bar'"); } - private void addDummyConstructor(ClassNode node) { + @Test + public void shouldDetectClassExtendsInterface() { + ClassNode node = new ClassNode("C", ACC_PUBLIC, ClassHelper.SERIALIZABLE_TYPE); + + verifier.visitClass(node); + + checkErrorCount(1); + checkErrorMessage("You are not allowed to extend the interface 'java.io.Serializable', use implements instead."); + } + + @Test + public void shouldDetectClassImplementsNonInterface() { + ClassNode[] impl = {ClassHelper.OBJECT_TYPE, ClassHelper.SERIALIZABLE_TYPE, ClassHelper.ELEMENT_TYPE_TYPE}; + ClassNode node = new ClassNode("C", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, impl, null); + + verifier.visitClass(node); + + checkErrorCount(2); + checkErrorMessage("You are not allowed to implement the class 'java.lang.Object', use extends instead."); + checkErrorMessage("You are not allowed to implement the class 'java.lang.annotation.ElementType', use extends instead."); + } + + //----------------------------------------------------------------------- + + private void addStaticConstructor(ClassNode node) { // constructors should not be treated as errors (they have no real meaning for interfaces anyway) node.addMethod(new MethodNode("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null)); } @@ -225,18 +242,16 @@ public class ClassCompletionVerifierTest extends TestSupport { private void checkErrorMessage(String expectedErrorMessage) { assertTrue("Expected an error message but none found.", source.getErrorCollector().hasErrors()); - assertTrue("Expected message to contain <" + expectedErrorMessage + - "> but was <" + flattenErrorMessage() + ">.", - flattenErrorMessage().contains(expectedErrorMessage)); + assertTrue("Expected message to contain <" + expectedErrorMessage + "> but was <" + flattenErrorMessage() + ">.", flattenErrorMessage().contains(expectedErrorMessage)); } private String flattenErrorMessage() { StringWriter stringWriter = new StringWriter(); - PrintWriter writer = new PrintWriter(stringWriter, true); - for (int i = source.getErrorCollector().getErrorCount() - 1; i >= 0; i--) { - source.getErrorCollector().getError(i).write(writer); + try (PrintWriter printWriter = new PrintWriter(stringWriter, true)) { + for (int i = source.getErrorCollector().getErrorCount() - 1; i >= 0; i -= 1) { + source.getErrorCollector().getError(i).write(printWriter); + } } - writer.close(); return stringWriter.toString(); } }
