blob: c215bba157812c2e66c9229ae0facd1c89cd3c00 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.javac;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.dev.javac.testing.impl.StaticJavaResource;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.UnitTestTreeLogger;
import junit.framework.TestCase;
import java.util.Collections;
/**
* Tests the JSORestrictionsChecker.
*/
public class JSORestrictionsTest extends TestCase {
public void testBaseClassFullyImplements() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface IntfA {\n");
goodCode.append(" void a();\n");
goodCode.append(" void b();\n");
goodCode.append(" }\n");
goodCode.append(" static interface IntfB {\n");
goodCode.append(" void c();\n");
goodCode.append(" }\n");
goodCode.append(" static abstract class BaseA extends JavaScriptObject {\n");
goodCode.append(" public final void a() { }\n");
goodCode.append(" protected BaseA() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class BaseB extends BaseA implements IntfA {\n");
goodCode.append(" public final void b() { }\n");
goodCode.append(" protected BaseB() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class LeafA extends BaseB {\n");
goodCode.append(" protected LeafA() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class LeafB extends BaseB implements IntfB {\n");
goodCode.append(" public final void c() { }\n");
goodCode.append(" protected LeafB() { }\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
/**
* Java's version of the 'diamond' type definition pattern. Both a subclass
* and superclass implement the same interface via two different chains of
* resolution (extended class and inherited interface) Not good style, but
* should be allowed.
*/
public void testDiamondInheritance() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" public interface Interface {\n");
goodCode.append(" void method();\n");
goodCode.append(" }\n");
goodCode.append(" public static abstract class CommonBase extends JavaScriptObject \n");
goodCode.append(" implements Interface {\n");
goodCode.append(" protected CommonBase() {}\n");
goodCode.append(" }\n");
goodCode.append(" public static class Impl extends CommonBase implements Interface {\n");
goodCode.append(" protected Impl() {}\n");
goodCode.append(" public final void method() {}\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
public void testFinalClass() {
StringBuffer code = new StringBuffer();
code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
code.append("final public class Buggy extends JavaScriptObject {\n");
code.append(" int nonfinal() { return 10; }\n");
code.append(" protected Buggy() { }\n");
code.append("}\n");
shouldGenerateNoError(code);
}
public void testImplementsInterfaces() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
goodCode.append(" static interface Squeaks2 extends Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" public void squeak2();\n");
goodCode.append(" }\n");
goodCode.append(" static class Squeaker extends JavaScriptObject implements Squeaks {\n");
goodCode.append(" public final void squeak() { }\n");
goodCode.append(" protected Squeaker() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class Squeaker2 extends Squeaker implements Squeaks, Squeaks2 {\n");
goodCode.append(" public final void squeak2() { }\n");
goodCode.append(" protected Squeaker2() { }\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
public void testInstanceField() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append(" protected Buggy() { }\n");
buggyCode.append(" int myStsate = 3;\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 4: "
+ JSORestrictionsChecker.ERR_INSTANCE_FIELD);
}
public void testMultiArgConstructor() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public final class Buggy extends JavaScriptObject {\n");
buggyCode.append(" protected Buggy(int howBuggy) { }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 3: "
+ JSORestrictionsChecker.ERR_CONSTRUCTOR_WITH_PARAMETERS);
}
public void testMultipleImplementations() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy {\n");
buggyCode.append(" static interface Squeaks {\n");
buggyCode.append(" public void squeak();\n");
buggyCode.append(" }\n");
buggyCode.append(" static class Squeaker extends JavaScriptObject implements Squeaks {\n");
buggyCode.append(" public final void squeak() { }\n");
buggyCode.append(" protected Squeaker() { }\n");
buggyCode.append(" }\n");
buggyCode.append(" static class Squeaker2 extends JavaScriptObject implements Squeaks {\n");
buggyCode.append(" public final void squeak() { }\n");
buggyCode.append(" protected Squeaker2() { }\n");
buggyCode.append(" }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 10: "
+ JSORestrictionsChecker.errAlreadyImplemented("Buggy$Squeaks",
"Buggy$Squeaker", "Buggy$Squeaker2"));
}
/**
* Normally, only a single JSO can implement an interface, but if all the
* implementations are in a common base class, that should be allowed.
*/
public void testMultipleImplementationsOk() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" public interface CommonInterface {\n");
goodCode.append(" void method();\n");
goodCode.append(" }\n");
goodCode.append(" public interface CommonInterfaceExtended extends CommonInterface {}\n");
goodCode.append(" public static class CommonBase extends JavaScriptObject\n");
goodCode.append(" implements CommonInterface {\n");
goodCode.append(" protected CommonBase() {}\n");
goodCode.append(" public final void method() {}\n");
goodCode.append(" }\n");
goodCode.append(" public static class Impl1 extends CommonBase\n");
goodCode.append(" implements CommonInterfaceExtended {\n");
goodCode.append(" protected Impl1() {}\n");
goodCode.append(" }\n");
goodCode.append(" public static class Impl2 extends CommonBase\n");
goodCode.append(" implements CommonInterfaceExtended {\n");
goodCode.append(" protected Impl2() {}\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
public void testNew() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy {\n");
buggyCode.append(" public static class MyJSO extends JavaScriptObject { \n");
buggyCode.append(" protected MyJSO() { }\n");
buggyCode.append(" }\n");
buggyCode.append(" MyJSO makeOne() { return new MyJSO(); }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 6: "
+ JSORestrictionsChecker.ERR_NEW_JSO);
}
public void testNoAnnotationOnInterfaceSubtype() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
goodCode.append(" static interface Sub extends Squeaks {\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
public void testNoConstructor() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append("}\n");
// The public constructor is implicit.
shouldGenerateError(buggyCode, "Line 2: "
+ JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR);
}
public void testNonEmptyConstructor() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append(" protected Buggy() { while(true) { } }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 3: "
+ JSORestrictionsChecker.ERR_NONEMPTY_CONSTRUCTOR);
}
public void testNonFinalMethod() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append(" int nonfinal() { return 10; }\n");
buggyCode.append(" protected Buggy() { }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 3: "
+ JSORestrictionsChecker.ERR_INSTANCE_METHOD_NONFINAL);
}
public void testNonJsoInterfaceExtension() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface Squeaks {\n");
goodCode.append(" public void squeak();\n");
goodCode.append(" }\n");
goodCode.append(" static interface Squeaks2 extends Squeaks {\n");
goodCode.append(" public void squeak2();\n");
goodCode.append(" }\n");
goodCode.append(" static class JsoSqueaker extends JavaScriptObject implements Squeaks {\n");
goodCode.append(" protected JsoSqueaker() {}\n");
goodCode.append(" public final void squeak() {}\n");
goodCode.append(" }\n");
goodCode.append(" static class JavaSqueaker2 implements Squeaks2 {\n");
goodCode.append(" protected JavaSqueaker2() {}\n");
goodCode.append(" public void squeak() {}\n");
goodCode.append(" public void squeak2() {}\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
public void testNonProtectedConstructor() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append(" Buggy() { }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 3: "
+ JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR);
}
public void testNonStaticInner() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy {\n");
buggyCode.append(" public class MyJSO extends JavaScriptObject {\n");
buggyCode.append(" protected MyJSO() { }\n");
buggyCode.append(" }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 3: "
+ JSORestrictionsChecker.ERR_IS_NONSTATIC_NESTED);
}
public void testNoOverride() {
StringBuffer buggyCode = new StringBuffer();
buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
buggyCode.append("public class Buggy extends JavaScriptObject {\n");
buggyCode.append(" protected Buggy() { }\n");
buggyCode.append(" public final Object clone() { return this; }\n");
buggyCode.append("}\n");
shouldGenerateError(buggyCode, "Line 4: "
+ JSORestrictionsChecker.ERR_OVERRIDDEN_METHOD);
}
public void testPrivateMethod() {
StringBuffer code = new StringBuffer();
code.append("import com.google.gwt.core.client.JavaScriptObject;\n");
code.append("public class Buggy extends JavaScriptObject {\n");
code.append(" private int nonfinal() { return 10; }\n");
code.append(" protected Buggy() { }\n");
code.append("}\n");
shouldGenerateNoError(code);
}
public void testTagInterfaces() {
StringBuffer goodCode = new StringBuffer();
goodCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
goodCode.append("public class Buggy {\n");
goodCode.append(" static interface Tag {}\n");
goodCode.append(" static interface Tag2 extends Tag {}\n");
goodCode.append(" static interface IntrExtendsTag extends Tag2 {\n");
goodCode.append(" public void intrExtendsTag();\n");
goodCode.append(" }\n");
goodCode.append(" static class Squeaker3 extends JavaScriptObject implements Tag {\n");
goodCode.append(" public final void squeak() { }\n");
goodCode.append(" protected Squeaker3() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class Squeaker4 extends JavaScriptObject implements Tag2 {\n");
goodCode.append(" public final void squeak() { }\n");
goodCode.append(" protected Squeaker4() { }\n");
goodCode.append(" }\n");
goodCode.append(" static class Squeaker5 extends JavaScriptObject implements IntrExtendsTag {\n");
goodCode.append(" public final void intrExtendsTag() { }\n");
goodCode.append(" protected Squeaker5() { }\n");
goodCode.append(" }\n");
goodCode.append("}\n");
shouldGenerateNoError(goodCode);
}
/**
* Test that when compiling buggyCode, the TypeOracleBuilder emits
* expectedError somewhere in its output. The code should define a class named
* Buggy.
*/
private void shouldGenerateError(CharSequence buggyCode,
String... expectedErrors) {
UnitTestTreeLogger.Builder builder = new UnitTestTreeLogger.Builder();
builder.setLowestLogLevel(TreeLogger.ERROR);
if (expectedErrors != null) {
builder.expectError("Errors in \'/mock/Buggy.java\'", null);
for (String e : expectedErrors) {
builder.expectError(e, null);
}
}
UnitTestTreeLogger logger = builder.createLogger();
StaticJavaResource buggyResource = new StaticJavaResource("Buggy",
buggyCode);
TypeOracleTestingUtils.buildStandardTypeOracleWith(logger,
Collections.<Resource> emptySet(),
CompilationStateTestBase.getGeneratedUnits(buggyResource));
logger.assertCorrectLogEntries();
}
private void shouldGenerateNoError(StringBuffer buggyCode) {
shouldGenerateError(buggyCode, (String[]) null);
}
}