/* * 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.shell.rewrite.client; import com.google.gwt.junit.client.GWTTestCase; import junit.framework.TestCase; /** * Test-case to check if the jsni blocks are mapped correctly between the * synthetic and anonymous classes generated by the javac and the jdt compiler. * <p> * This issue arises when emma.jar is in the classpath. Gwt then follows the * emma strategy and loads (offline-instrumented) classes from the disk. These * classes could have been generated by any java compiler -- the java compiler * does not have to be jdt and frequently is javac. These class files may * contain references to synthetic classes and anonymous classes that the jdt * does not know about. This testcase checks that gwt handles these cases * correctly. */ public class EmmaClassLoadingTest extends GWTTestCase { enum EnumClass { A, B, C, } interface TestInterface { void foo(); } private static String messages[] = { "1a foo", "1b foo", "1enum A", "1d foo", "1e foo"}; private static int logCount = 0; private static void log(String msg) { assertEquals(messages[logCount++], msg); } @Override public String getModuleName() { return "com.google.gwt.dev.shell.rewrite.EmmaClassLoadingTest"; } public void test1() { TestInterface a = new TestInterface() { public void foo() { log("1a foo"); } }; a.foo(); TestInterface b = new TestInterface() { public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1b foo"); }-*/; }; b.foo(); if (false) { TestInterface c = new TestInterface() { public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("ANY_FOO_1"); }-*/; }; } EnumClass et = EnumClass.A; switch (et) { case A: log("1enum A"); break; case B: log("ANY_FOO_2"); break; case C: log("ANY_FOO_3"); break; } TestInterface d = new TestInterface() { public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1d foo"); }-*/; }; d.foo(); /* * jdt generates $1 (a), $2 (b), $3 (d), $4 (e). javac generates $1 (a), $2 * (b), $3 (c), $4 (d), $5 (e), $6 (synthetic). Added e so that the test * fails quickly. Otherwise, it had to wait for a time-out (in looking * through jdt generated code for non-existent jsni methods of $4) to fail. */ TestInterface e = new TestInterface() { public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1e foo"); }-*/; }; e.foo(); } public void test2() { // SecondTopLevelClass second = new SecondTopLevelClass(); SecondTopLevelClass.InnerClass.test(); } public void test3() { ThirdTopLevelClass third = new ThirdTopLevelClass(); third.test(); } public void test4() { FourthTopLevelClass fourth = new FourthTopLevelClass(); fourth.test(); } } /** * Check that the algorithm correctly maps named inner classes. In this example, * jdt generates $1Foo and $2Foo whereas javac generates $2Foo and $3Foo. * */ class FourthTopLevelClass extends TestCase { private static String messages[] = {"4a foo"}; private static int logCount = 0; @SuppressWarnings("unused") // used by JSNI private static void log(String msg) { assertEquals(messages[logCount++], msg); } void test() { test1(); test2(); }; private void test1() { if (false) { class Foo { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("ANY_FOO"); }-*/; } new Foo().foo(); } } private void test2() { class Foo { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("4a foo"); }-*/; } new Foo().foo(); } /* * Added a test3() method so that when the test fails, it fails quickly. * Instead of timing out, the AssertEquals fails */ @SuppressWarnings("unused") private void test3() { class Foo { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("4b foo"); }-*/; } new Foo().foo(); } } /** * Check if GWT is able to correctly compile cases when there are multiple * top-level classes and when there is a need to traverse inner classes. This * class's test method simply mirrors the test methods of EmmaClassLoadingTest. * */ class SecondTopLevelClass extends TestCase { static class InnerClass { /** * Test that mapping is constructed for something which is not in the main * unit as well. */ static void test() { EmmaClassLoadingTest.TestInterface a = new EmmaClassLoadingTest.TestInterface() { public void foo() { log("2a foo"); } }; a.foo(); EmmaClassLoadingTest.TestInterface b = new EmmaClassLoadingTest.TestInterface() { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2b foo"); }-*/; }; b.foo(); if (false) { EmmaClassLoadingTest.TestInterface c = new EmmaClassLoadingTest.TestInterface() { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("ANY_FOO_1"); }-*/; }; } EmmaClassLoadingTest.EnumClass et = EmmaClassLoadingTest.EnumClass.A; switch (et) { case A: log("2enum A"); break; case B: log("ANY_FOO_2"); break; case C: log("ANY_FOO_3"); break; } EmmaClassLoadingTest.TestInterface d = new EmmaClassLoadingTest.TestInterface() { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2d foo"); }-*/; }; d.foo(); /* * jdt generates $1 (a), $2 (b), $3 (d), $4 (e). javac generates $1 (a), * $2 (b), $3 (c), $4 (d), $5 (e), $6 (synthetic). Added e so that the * test fails quickly. Otherwise, it had to wait for a time-out (in * looking through jdt generated code for non-existent jsni methods of $4) * to fail. */ EmmaClassLoadingTest.TestInterface e = new EmmaClassLoadingTest.TestInterface() { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2e foo"); }-*/; }; e.foo(); } } private static String messages[] = { "2a foo", "2b foo", "2enum A", "2d foo", "2e foo"}; private static int logCount = 0; private static void log(String msg) { assertEquals(messages[logCount++], msg); } } /** * Check that the mapping algorithm is not confused by the presence of other * inner classes. */ class ThirdTopLevelClass extends TestCase { private static String messages[] = {"3a foo"}; private static int logCount = 0; @SuppressWarnings("unused") // called by JSNI private static void log(String msg) { assertEquals(messages[logCount++], msg); } void test() { EmmaClassLoadingTest.TestInterface a = new EmmaClassLoadingTest.TestInterface() { @SuppressWarnings("jsni") public native void foo() /*-{ @com.google.gwt.dev.shell.rewrite.client.ThirdTopLevelClass::log(Ljava/lang/String;)("3a foo"); }-*/; }; a.foo(); } }