| /* |
| * Copyright 2013 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.core.interop; |
| |
| import static jsinterop.annotations.JsPackage.GLOBAL; |
| |
| import com.google.gwt.core.client.JavaScriptObject; |
| import com.google.gwt.junit.client.GWTTestCase; |
| |
| import java.util.Iterator; |
| |
| import jsinterop.annotations.JsFunction; |
| import jsinterop.annotations.JsMethod; |
| import jsinterop.annotations.JsPackage; |
| import jsinterop.annotations.JsProperty; |
| import jsinterop.annotations.JsType; |
| |
| /** |
| * Tests JsType functionality. |
| */ |
| @SuppressWarnings("cast") |
| public class JsTypeTest extends GWTTestCase { |
| |
| @Override |
| public String getModuleName() { |
| return "com.google.gwt.core.Interop"; |
| } |
| |
| public void testConcreteJsTypeAccess() { |
| ConcreteJsType concreteJsType = new ConcreteJsType(); |
| |
| assertJsTypeHasFields(concreteJsType, "publicMethod", "publicField"); |
| assertJsTypeDoesntHaveFields(concreteJsType, "publicStaticMethod", "privateMethod", |
| "protectedMethod", "packageMethod", "publicStaticField", "privateField", "protectedField", |
| "packageField"); |
| |
| assertEquals(10, callIntFunction(concreteJsType, "publicMethod")); |
| } |
| |
| public void testAbstractJsTypeAccess() { |
| AbstractJsType jsType = new AbstractJsType() { |
| @Override |
| public int publicMethod() { |
| return 32; |
| } |
| }; |
| |
| assertJsTypeHasFields(jsType, "publicMethod"); |
| assertEquals(32, callIntFunction(jsType, "publicMethod")); |
| assertEquals(32, jsType.publicMethod()); |
| } |
| |
| public void testConcreteJsTypeSubclassAccess() { |
| ConcreteJsTypeSubclass concreteJsTypeSubclass = new ConcreteJsTypeSubclass(); |
| |
| // A subclass of a JsType is not itself a JsType. |
| assertJsTypeDoesntHaveFields(concreteJsTypeSubclass, "publicSubclassMethod", |
| "publicSubclassField", "publicStaticSubclassMethod", "privateSubclassMethod", |
| "protectedSubclassMethod", "packageSubclassMethod", "publicStaticSubclassField", |
| "privateSubclassField", "protectedSubclassField", "packageSubclassField"); |
| |
| // But if it overrides an exported method then the overriding method will be exported. |
| assertJsTypeHasFields(concreteJsTypeSubclass, "publicMethod"); |
| |
| assertEquals(20, callIntFunction(concreteJsTypeSubclass, "publicMethod")); |
| assertEquals(10, concreteJsTypeSubclass.publicSubclassMethod()); |
| } |
| |
| @JsType |
| enum JsTypeEnum { |
| JSVALUE0, |
| JSVALUE1; |
| } |
| |
| public void testJsTypeEnum() { |
| JsTypeEnum value = JsTypeEnum.JSVALUE1; |
| |
| assertEquals(value.ordinal(), ((TestAccessor) this).callJsTypeEmumOrdinalMethod(value)); |
| } |
| |
| // Obscure the call with an alias so that the call is not detected by EnumOrdinalizer. |
| @JsType(isNative = true) |
| private interface TestAccessor { |
| @JsMethod |
| // Receive the JsType enum as its own type so that it is not seen as an upcast by |
| // EnumOrdinalizer, but actually dispach to a method that takes Object. |
| int callJsTypeEmumOrdinalMethod(JsTypeEnum value); |
| } |
| |
| @JsMethod |
| private int callJsTypeEmumOrdinalMethod(Object value) { |
| return callIntFunction(value, "ordinal"); |
| } |
| |
| public void testConcreteJsTypeNoTypeTightenField() { |
| // If we type-tighten, java side will see no calls and think that field could only AImpl1. |
| ConcreteJsType concreteJsType = new ConcreteJsType(); |
| setTheField(concreteJsType, new ConcreteJsType.AImpl2()); |
| assertEquals(101, concreteJsType.notTypeTightenedField.x()); |
| } |
| |
| @JsType |
| interface A { |
| boolean m(Object o); |
| } |
| |
| private static class AImpl implements A { |
| @Override |
| public boolean m(Object o) { |
| return o == null; |
| } |
| } |
| |
| public void testNativeMethodOverrideNoTypeTightenParam() { |
| AImpl a = new AImpl(); |
| assertTrue(a.m(null)); |
| assertFalse((Boolean) callFunction(a, "m", new Object())); |
| } |
| |
| private native void setTheField(ConcreteJsType obj, ConcreteJsType.A value)/*-{ |
| obj.notTypeTightenedField = value; |
| }-*/; |
| |
| public void testRevealedOverrideJsType() { |
| PlainParentType plainParentType = new PlainParentType(); |
| RevealedOverrideSubType revealedOverrideSubType = new RevealedOverrideSubType(); |
| |
| // PlainParentType is neither @JsType or @JsType and so exports no functions. |
| assertFalse(hasField(plainParentType, "run")); |
| |
| // RevealedOverrideSubType defines no functions itself, it only inherits them, but it still |
| // exports run() because it implements the @JsType interface JsTypeRunnable. |
| assertTrue(hasField(revealedOverrideSubType, "run")); |
| |
| ConcreteJsTypeJsSubclass subclass = new ConcreteJsTypeJsSubclass(); |
| assertEquals(100, subclass.publicMethodAlsoExposedAsNonJsMethod()); |
| SubclassInterface subclassInterface = subclass; |
| assertEquals(100, subclassInterface.publicMethodAlsoExposedAsNonJsMethod()); |
| } |
| |
| @JsType(isNative = true) |
| interface MyNativeJsTypeInterface { |
| } |
| |
| class MyNativeJsTypeInterfaceImpl implements MyNativeJsTypeInterface { |
| } |
| |
| public void testCasts() { |
| Object myClass; |
| assertNotNull(myClass = (ElementLikeNativeInterface) createMyNativeJsType()); |
| assertNotNull(myClass = (MyNativeJsTypeInterface) createMyNativeJsType()); |
| assertNotNull(myClass = (HTMLElementConcreteNativeJsType) createNativeButton()); |
| |
| try { |
| assertNotNull(myClass = (HTMLElementConcreteNativeJsType) createMyNativeJsType()); |
| fail(); |
| } catch (ClassCastException cce) { |
| // Expected. |
| } |
| |
| // Test cross cast for native types |
| Object nativeButton1 = (HTMLElementConcreteNativeJsType) createNativeButton(); |
| Object nativeButton2 = (HTMLElementAnotherConcreteNativeJsType) nativeButton1; |
| |
| /* |
| * If the optimizations are turned on, it is possible for the compiler to dead-strip the |
| * variables since they are not used. Therefore the casts could potentially be stripped. |
| */ |
| assertNotNull(myClass); |
| assertNotNull(nativeButton1); |
| assertNotNull(nativeButton2); |
| } |
| |
| /** |
| * A test class marked with JsType but isn't referenced from any Java code except instanceof. |
| */ |
| @JsType(isNative = true) |
| public interface MyNativeJsTypeInterfaceAndOnlyInstanceofReference { |
| } |
| |
| /** |
| * A test class marked with JsType but isn't referenced from any Java code except instanceof. |
| */ |
| @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Error") |
| public static class AliasToMyNativeJsTypeWithOnlyInstanceofReference { |
| } |
| |
| public void testInstanceOf_nativeJsType() { |
| Object object = createMyNativeJsType(); |
| |
| assertTrue(object instanceof Object); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertTrue(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertTrue(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| public void testInstanceOf_jsoWithoutProto() { |
| Object object = JavaScriptObject.createObject(); |
| |
| assertTrue(object instanceof Object); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertFalse(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertFalse(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| public void testInstanceOf_jsoWithNativeButtonProto() { |
| Object object = createNativeButton(); |
| |
| assertTrue(object instanceof Object); |
| assertTrue(object instanceof HTMLElementConcreteNativeJsType); |
| assertTrue(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertTrue(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertFalse(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertFalse(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| public void testInstanceOf_implementsJsType() { |
| // Foils type tightening. |
| Object object = new ElementLikeNativeInterfaceImpl(); |
| |
| assertTrue(object instanceof Object); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertFalse(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertTrue(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertFalse(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| public void testInstanceOf_implementsJsTypeWithPrototype() { |
| // Foils type tightening. |
| Object object = new MyNativeJsTypeInterfaceImpl(); |
| |
| assertTrue(object instanceof Object); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertFalse(object instanceof MyNativeJsType); |
| assertTrue(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertFalse(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| public void testInstanceOf_concreteJsType() { |
| // Foils type tightening. |
| Object object = new ConcreteJsType(); |
| |
| assertTrue(object instanceof Object); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertFalse(object instanceof Iterator); |
| assertFalse(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertFalse(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertTrue(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| @JsType(isNative = true, namespace = GLOBAL, name = "Error") |
| static class MyNativeJsType { } |
| |
| static class MyNativeJsTypeSubclass extends MyNativeJsType { } |
| |
| static class MyNativeJsTypeSubclassWithIterator extends MyNativeJsType implements Iterable { |
| @Override |
| public Iterator iterator() { |
| return null; |
| } |
| } |
| |
| public void testInstanceOf_extendsNativeJsType() { |
| // Foils type tightening. |
| Object object = new MyNativeJsTypeSubclassWithIterator(); |
| |
| assertTrue(object instanceof Object); |
| // TODO(rluble): uncomment this when native JsType subclasses are setup correctly. |
| // assertTrue(object instanceof MyNativeJsType); |
| assertFalse(object instanceof MyNativeJsTypeSubclass); |
| assertTrue(object instanceof MyNativeJsTypeSubclassWithIterator); |
| assertFalse(object instanceof HTMLElementConcreteNativeJsType); |
| assertFalse(object instanceof HTMLElementAnotherConcreteNativeJsType); |
| assertFalse(object instanceof HTMLButtonElementConcreteNativeJsType); |
| assertTrue(object instanceof Iterable); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl); |
| assertFalse(object instanceof ElementLikeNativeInterfaceImpl); |
| assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference); |
| assertTrue(object instanceof AliasToMyNativeJsTypeWithOnlyInstanceofReference); |
| assertFalse(object instanceof ConcreteJsType); |
| assertFalse(object instanceof MyNativeJsTypeInterface[]); |
| assertFalse(object instanceof MyNativeJsTypeInterfaceImpl[][]); |
| } |
| |
| @JsType(isNative = true, namespace = "testfoo.bar") |
| static class MyNamespacedNativeJsType { |
| } |
| |
| public void testInstanceOf_withNameSpace() { |
| Object obj1 = createMyNamespacedJsInterface(); |
| Object obj2 = createMyWrongNamespacedJsInterface(); |
| |
| assertTrue(obj1 instanceof MyNamespacedNativeJsType); |
| assertFalse(obj1 instanceof MyNativeJsType); |
| |
| assertFalse(obj2 instanceof MyNamespacedNativeJsType); |
| } |
| |
| public void testEnumeration() { |
| assertEquals(2, callPublicMethodFromEnumeration(MyEnumWithJsType.TEST1)); |
| assertEquals(3, callPublicMethodFromEnumeration(MyEnumWithJsType.TEST2)); |
| } |
| |
| public void testEnumJsTypeAccess() { |
| assertJsTypeHasFields(MyEnumWithJsType.TEST2, "publicMethod", "publicField"); |
| assertJsTypeDoesntHaveFields(MyEnumWithJsType.TEST2, "publicStaticMethod", "privateMethod", |
| "protectedMethod", "packageMethod", "publicStaticField", "privateField", "protectedField", |
| "packageField"); |
| } |
| |
| public void testEnumSubclassEnumeration() { |
| assertEquals(100, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.A)); |
| assertEquals(200, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.B)); |
| assertEquals(1, callPublicMethodFromEnumerationSubclass(MyEnumWithSubclassGen.C)); |
| } |
| |
| private static native int callIntFunction(Object object, String functionName) /*-{ |
| return object[functionName](); |
| }-*/; |
| |
| private static native Object callFunction(Object object, String functionName, Object param) /*-{ |
| return object[functionName](param); |
| }-*/; |
| |
| private static native Object createNativeButton() /*-{ |
| return $doc.createElement("button"); |
| }-*/; |
| |
| private static native Object createMyNativeJsType() /*-{ |
| return new $wnd.Error(); |
| }-*/; |
| |
| private static native Object createMyNamespacedJsInterface() /*-{ |
| $wnd.testfoo = {}; |
| $wnd.testfoo.bar = {}; |
| $wnd.testfoo.bar.MyNamespacedNativeJsType = function(){}; |
| return new $wnd.testfoo.bar.MyNamespacedNativeJsType(); |
| }-*/; |
| |
| private static native Object createMyWrongNamespacedJsInterface() /*-{ |
| $wnd["testfoo.bar.MyNamespacedNativeJsType"] = function(){}; |
| return new $wnd['testfoo.bar.MyNamespacedNativeJsType'](); |
| }-*/; |
| |
| private static native boolean isUndefined(int value) /*-{ |
| return value === undefined; |
| }-*/; |
| |
| private static native boolean hasField(Object object, String fieldName) /*-{ |
| return object[fieldName] != undefined; |
| }-*/; |
| |
| private static native int callPublicMethodFromEnumeration(MyEnumWithJsType enumeration) /*-{ |
| return enumeration.idxAddOne(); |
| }-*/; |
| |
| private static native int callPublicMethodFromEnumerationSubclass( |
| MyEnumWithSubclassGen enumeration) /*-{ |
| return enumeration.foo(); |
| }-*/; |
| |
| public static void assertJsTypeHasFields(Object obj, String... fields) { |
| for (String field : fields) { |
| assertTrue("Field '" + field + "' should be exported", hasField(obj, field)); |
| } |
| } |
| |
| public static void assertJsTypeDoesntHaveFields(Object obj, String... fields) { |
| for (String field : fields) { |
| assertFalse("Field '" + field + "' should not be exported", hasField(obj, field)); |
| } |
| } |
| |
| @JsType |
| interface SimpleJsTypeFieldInterface { |
| } |
| |
| static class SimpleJsTypeFieldClass implements SimpleJsTypeFieldInterface { |
| } |
| |
| static class SimpleJsTypeWithField { |
| @JsProperty |
| public SimpleJsTypeFieldInterface someField; |
| } |
| |
| public void testJsTypeField() { |
| assertTrue(new SimpleJsTypeFieldClass() != new SimpleJsTypeFieldClass()); |
| SimpleJsTypeWithField holder = new SimpleJsTypeWithField(); |
| fillJsTypeField(holder); |
| SimpleJsTypeFieldInterface someField = holder.someField; |
| assertNotNull(someField); |
| } |
| |
| private native void fillJsTypeField(SimpleJsTypeWithField jstype) /*-{ |
| jstype.someField = {}; |
| }-*/; |
| |
| @JsType(isNative = true) |
| interface InterfaceWithSingleJavaConcrete { |
| int m(); |
| } |
| |
| static class JavaConcrete implements InterfaceWithSingleJavaConcrete { |
| public int m() { |
| return 5; |
| } |
| } |
| |
| private native Object nativeObjectImplementingM() /*-{ |
| return {m: function() { return 3;} } |
| }-*/; |
| |
| public void testSingleJavaConcreteInterface() { |
| // Create a couple of instances and use the objects in some way to avoid complete pruning |
| // of JavaConcrete |
| assertTrue(new JavaConcrete() != new JavaConcrete()); |
| assertSame(5, new JavaConcrete().m()); |
| assertSame(3, ((InterfaceWithSingleJavaConcrete) nativeObjectImplementingM()).m()); |
| } |
| |
| @JsFunction |
| interface JsFunctionInterface { |
| int m(); |
| } |
| |
| static final class JavaConcreteJsFunction implements JsFunctionInterface { |
| public int m() { |
| return 5; |
| } |
| } |
| |
| private native Object nativeJsFunction() /*-{ |
| return function() { return 3;}; |
| }-*/; |
| |
| public void testSingleJavaConcreteJsFunction() { |
| // Create a couple of instances and use the objects in some way to avoid complete pruning |
| // of JavaConcrete |
| assertTrue(new JavaConcreteJsFunction() != new JavaConcreteJsFunction()); |
| assertSame(5, new JavaConcreteJsFunction().m()); |
| assertSame(3, ((JsFunctionInterface) nativeJsFunction()).m()); |
| } |
| |
| @JsType |
| static abstract class SomeAbstractClass { |
| public abstract SomeAbstractClass m(); |
| } |
| |
| // Do not rename this class. |
| @JsType |
| static abstract class SomeZAbstractSubclass extends SomeAbstractClass { |
| public abstract SomeZAbstractSubclass m(); |
| } |
| |
| @JsType |
| static class SomeConcreteSubclass extends SomeZAbstractSubclass { |
| public SomeConcreteSubclass m() { |
| return this; |
| } |
| } |
| |
| public void testNamedBridge() { |
| // Bridges are sorted by signature in the JDT. Make sure that the bridge method appears second. |
| assertTrue( |
| SomeConcreteSubclass.class.getName().compareTo(SomeZAbstractSubclass.class.getName()) < 0); |
| SomeConcreteSubclass o = new SomeConcreteSubclass(); |
| assertEquals(o, o.m()); |
| } |
| |
| static class NonPublicJsMethodClass { |
| @JsMethod private String foo() { return "foo"; } |
| @JsMethod String bar() { return "bar"; } |
| } |
| |
| public void testJsMethodWithDifferentVisiblities() { |
| NonPublicJsMethodClass instance = new NonPublicJsMethodClass(); |
| assertEquals("foo", instance.foo()); |
| assertEquals("bar", instance.bar()); |
| assertEquals("foo", callFunction(instance, "foo", null)); |
| assertEquals("bar", callFunction(instance, "bar", null)); |
| } |
| |
| @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "*") |
| interface Star { |
| } |
| |
| public void testStar() { |
| Object object = new Object(); |
| |
| assertNotNull((Star) object); |
| |
| object = Double.valueOf(3.0); |
| assertNotNull((Star) object); |
| } |
| |
| @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "?") |
| interface Wildcard { |
| } |
| |
| public void testWildcard() { |
| Object object = new Object(); |
| |
| assertNotNull((Wildcard) object); |
| |
| object = Double.valueOf(3.0); |
| assertNotNull((Wildcard) object); |
| } |
| |
| static class ClassWithJsMethod { |
| @JsMethod(name = "name") |
| public String className() { |
| return ClassWithJsMethod.class.getName(); |
| } |
| } |
| |
| static class ClassWithJsMethodInheritingName extends ClassWithJsMethod { |
| @JsMethod |
| public String className() { |
| return ClassWithJsMethodInheritingName.class.getName(); |
| } |
| } |
| |
| private native String callName(Object o) /*-{ |
| return o.name(); |
| }-*/; |
| |
| public void testInheritName() { |
| ClassWithJsMethod object = new ClassWithJsMethod(); |
| assertEquals(ClassWithJsMethod.class.getName(), object.className()); |
| assertEquals(ClassWithJsMethod.class.getName(), callName(object)); |
| |
| object = new ClassWithJsMethodInheritingName(); |
| assertEquals(ClassWithJsMethodInheritingName.class.getName(), object.className()); |
| assertEquals(ClassWithJsMethodInheritingName.class.getName(), callName(object)); |
| } |
| } |