001: package org.codehaus.groovy.classgen;
002:
003: import org.codehaus.groovy.ast.*;
004: import org.codehaus.groovy.control.SourceUnit;
005:
006: import java.io.PrintWriter;
007: import java.io.StringWriter;
008:
009: /**
010: *
011: * @author Paul King
012: */
013: public class ClassCompletionVerifierTest extends TestSupport {
014: private SourceUnit source;
015: private ClassCompletionVerifier verifier;
016: private static final String ABSTRACT_FINAL_CLASS = "AbstractFinalClass";
017: private static final String FINAL_INTERFACE = "FinalInterface";
018: private static final String EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE = "The class '"
019: + ABSTRACT_FINAL_CLASS
020: + "' must not be both final and abstract.";
021: private static final String EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE = "The interface '"
022: + FINAL_INTERFACE
023: + "' must not be final. It is by definition abstract.";
024: private static final String EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE = "The method 'xxx' from interface 'zzz' must not be final. It is by definition abstract.";
025: private static final String EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE = "The method 'yyy' from interface 'zzz' must not be static. Only fields may be static in an interface.";
026: private static final String EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE = "The class 'DodgyClass' has an incorrect modifier transient.";
027: private static final String EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE = "The class 'DodgyClass' has an incorrect modifier volatile.";
028: private static final String EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE = "Repetitive method name/signature for method 'xxx' in class 'zzz'.";
029: private static final String EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE = "Repetitive method name/signature for method 'xxx' in interface 'zzz'.";
030:
031: protected void setUp() throws Exception {
032: super .setUp();
033: source = SourceUnit.create("dummy.groovy", "");
034: verifier = new ClassCompletionVerifier(source);
035: }
036:
037: public void testDetectsFinalAbstractClass() throws Exception {
038: checkVisitErrors("FinalClass", ACC_FINAL, false);
039: checkVisitErrors("AbstractClass", ACC_ABSTRACT, false);
040: checkVisitErrors(ABSTRACT_FINAL_CLASS,
041: ACC_ABSTRACT | ACC_FINAL, true);
042: checkErrorMessage(EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE);
043: }
044:
045: public void testDetectsDuplicateMethodsForClassNoParams()
046: throws Exception {
047: checkDetectsDuplicateMethods(0,
048: EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE,
049: Parameter.EMPTY_ARRAY);
050: }
051:
052: public void testDetectsDuplicateMethodsForInterfaceOneParam()
053: throws Exception {
054: Parameter[] stringParam = { new Parameter(
055: ClassHelper.STRING_TYPE, "x") };
056: checkDetectsDuplicateMethods(ACC_INTERFACE,
057: EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE,
058: stringParam);
059: }
060:
061: private void checkDetectsDuplicateMethods(int modifiers,
062: String expectedErrorMessage, Parameter[] params) {
063: ClassNode node = new ClassNode("zzz", modifiers,
064: ClassHelper.OBJECT_TYPE);
065: node.addMethod(new MethodNode("xxx", ACC_PUBLIC,
066: ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY,
067: null));
068: node.addMethod(new MethodNode("xxx", ACC_PUBLIC,
069: ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY,
070: null));
071: verifier.visitClass(node);
072: checkErrorCount(2);
073: checkErrorMessage(expectedErrorMessage);
074: }
075:
076: public void testDetectsIncorrectOtherModifier() throws Exception {
077: checkVisitErrors("DodgyClass", ACC_TRANSIENT | ACC_VOLATILE,
078: true);
079: checkErrorMessage(EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE);
080: checkErrorMessage(EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE);
081: }
082:
083: public void testDetectsFinalAbstractInterface() throws Exception {
084: checkVisitErrors(FINAL_INTERFACE, ACC_ABSTRACT | ACC_FINAL
085: | ACC_INTERFACE, true);
086: checkErrorMessage(EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE);
087: }
088:
089: public void testDetectsFinalAndStaticMethodsInInterface()
090: throws Exception {
091: ClassNode node = new ClassNode("zzz", ACC_ABSTRACT
092: | ACC_INTERFACE, ClassHelper.OBJECT_TYPE);
093: node.addMethod(new MethodNode("xxx", ACC_PUBLIC | ACC_FINAL,
094: ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY,
095: ClassNode.EMPTY_ARRAY, null));
096: node.addMethod(new MethodNode("yyy", ACC_PUBLIC | ACC_STATIC,
097: ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY,
098: ClassNode.EMPTY_ARRAY, null));
099: // constructors should not be treated as errors (they have no real meaning for interfaces anyway)
100: node.addMethod(new MethodNode("<clinit>", ACC_PUBLIC
101: | ACC_STATIC, ClassHelper.OBJECT_TYPE,
102: Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null));
103: verifier.visitClass(node);
104: checkErrorCount(2);
105: checkErrorMessage(EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE);
106: checkErrorMessage(EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE);
107: }
108:
109: private void checkErrorCount(int count) {
110: assertEquals(buildErrorMessage(count), count, source
111: .getErrorCollector().getErrorCount());
112: }
113:
114: private String buildErrorMessage(int count) {
115: StringBuffer sb = new StringBuffer();
116: sb.append("Expected ").append(count);
117: sb.append(" error messages but found ");
118: sb.append(source.getErrorCollector().getErrorCount()).append(
119: ":\n");
120: sb.append(flattenErrorMessage());
121: return sb.toString();
122: }
123:
124: private void checkVisitErrors(String name, int modifiers,
125: boolean expectedToFail) {
126: ClassNode node = new ClassNode(name, modifiers,
127: ClassHelper.OBJECT_TYPE);
128: verifier.visitClass(node);
129: assertTrue(source.getErrorCollector().hasErrors() == expectedToFail);
130: }
131:
132: private void checkErrorMessage(String expectedErrorMessage) {
133: assertTrue("Expected an error message but none found.", source
134: .getErrorCollector().hasErrors());
135: assertTrue("Expected message to contain <"
136: + expectedErrorMessage + "> but was <"
137: + flattenErrorMessage() + ">.", flattenErrorMessage()
138: .indexOf(expectedErrorMessage) != -1);
139: }
140:
141: private String flattenErrorMessage() {
142: StringWriter stringWriter = new StringWriter();
143: PrintWriter writer = new PrintWriter(stringWriter, true);
144: for (int i = source.getErrorCollector().getErrorCount() - 1; i >= 0; i--) {
145: source.getErrorCollector().getError(i).write(writer);
146: }
147: writer.close();
148: return stringWriter.toString();
149: }
150: }
|