Revision: 7322 Author: b...@google.com Date: Wed Dec 16 17:16:05 2009 Log: Add class and method resymbolization to JUnitShell to improve production-mode stack traces for continuous builds. Turns on emulated stack traces across all browsers to ensure consistent behavior when running GWTTestCases.
Patch by: bobv Review by: jat, scottb, rjrjr http://code.google.com/p/google-web-toolkit/source/detail?r=7322 Added: /trunk/user/src/com/google/gwt/junit/linker /trunk/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java Deleted: /trunk/user/test/com/google/gwt/core/client/impl/EmulatedStackTraceTest.java Modified: /trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java /trunk/user/src/com/google/gwt/junit/JUnit.gwt.xml /trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java /trunk/user/test/com/google/gwt/core/CoreSuite.java /trunk/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java ======================================= --- /dev/null +++ /trunk/user/src/com/google/gwt/junit/linker/JUnitSymbolMapsLinker.java Wed Dec 16 17:16:05 2009 @@ -0,0 +1,46 @@ +/* + * Copyright 2009 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.junit.linker; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.CompilationResult; +import com.google.gwt.core.ext.linker.EmittedArtifact; +import com.google.gwt.core.linker.SymbolMapsLinker; + +import java.io.ByteArrayOutputStream; + +/** + * Emits the symbol maps into the application output directory so that the + * JUnitHostImpl servlet can read them. + */ +public class JUnitSymbolMapsLinker extends SymbolMapsLinker { + @Override + protected void doEmitSymbolMap(TreeLogger logger, ArtifactSet artifacts, + CompilationResult result, ByteArrayOutputStream out) + throws UnableToCompleteException { + // Collaborate with JUnitHostImpl.loadSymbolMap + String partialPath = ".junit_symbolMaps/" + result.getStrongName() + + STRONG_NAME_SUFFIX; + + EmittedArtifact symbolMapArtifact = emitBytes(logger, out.toByteArray(), + partialPath); + + artifacts.add(symbolMapArtifact); + } + +} ======================================= --- /trunk/user/test/com/google/gwt/core/client/impl/EmulatedStackTraceTest.java Tue Jul 28 09:27:08 2009 +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2009 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.client.impl; - -/** - * Reuses the existing StackTraceCreatorTest, but forces emulated JS stack trace - * code to be emitted.. - */ -public class EmulatedStackTraceTest extends StackTraceCreatorTest { - - @Override - public String getModuleName() { - return "com.google.gwt.core.EmulateJsStack"; - } - -} ======================================= --- /trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java Wed Oct 21 13:20:55 2009 +++ /trunk/dev/core/src/com/google/gwt/core/linker/IFrameLinker.java Wed Dec 16 17:16:05 2009 @@ -259,7 +259,7 @@ out.newlineOpt(); // Setup the well-known variables. - out.print("<head><script>"); + out.print("<head><meta charset=\"UTF-8\" /><script>"); out.newlineOpt(); out.print("var $gwt_version = \"" + About.getGwtVersionNum() + "\";"); out.newlineOpt(); ======================================= --- /trunk/user/src/com/google/gwt/junit/JUnit.gwt.xml Wed Aug 19 11:04:00 2009 +++ /trunk/user/src/com/google/gwt/junit/JUnit.gwt.xml Wed Dec 16 17:16:05 2009 @@ -31,15 +31,12 @@ <when-type-assignable class="com.google.gwt.junit.client.GWTTestCase"/> </generate-with> - <!-- We want to provide good stack traces on browsers that don't provide good native stack traces. --> + <!-- We want to provide consistent stack traces across all browsers. --> <set-configuration-property name="compiler.emulatedStack.recordFileNames" value="true" /> - <set-property name="compiler.emulatedStack" value="true" > - <none> - <when-property-is name="user.agent" value="gecko" /> - <when-property-is name="user.agent" value="gecko1_8" /> - <when-property-is name="user.agent" value="opera" /> - </none> - </set-property> + <set-property name="compiler.emulatedStack" value="true" /> + + <!-- Override the regular symbolMaps linker to put the data somewhere we can find it --> + <define-linker name="symbolMaps" class="com.google.gwt.junit.linker.JUnitSymbolMapsLinker" /> <servlet path='/junithost' class='com.google.gwt.junit.server.JUnitHostImpl'/> ======================================= --- /trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java Tue Nov 24 12:23:12 2009 +++ /trunk/user/src/com/google/gwt/junit/server/JUnitHostImpl.java Wed Dec 16 17:16:05 2009 @@ -15,6 +15,8 @@ */ package com.google.gwt.junit.server; +import com.google.gwt.dev.util.JsniRef; +import com.google.gwt.dev.util.StringKey; import com.google.gwt.junit.JUnitFatalLaunchException; import com.google.gwt.junit.JUnitMessageQueue; import com.google.gwt.junit.JUnitShell; @@ -28,10 +30,14 @@ import com.google.gwt.user.server.rpc.HybridServiceServlet; import com.google.gwt.user.server.rpc.RPCServletUtils; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; @@ -45,6 +51,18 @@ */ public class JUnitHostImpl extends HybridServiceServlet implements JUnitHost { + private static class StrongName extends StringKey { + protected StrongName(String value) { + super(value); + } + } + + private static class SymbolName extends StringKey { + protected SymbolName(String value) { + super(value); + } + } + /** * A hook into GWTUnitTestShell, the underlying unit test process. */ @@ -87,6 +105,8 @@ fld.setAccessible(true); fld.set(obj, value); } + + private Map<StrongName, Map<SymbolName, String>> symbolMaps = new HashMap<StrongName, Map<SymbolName, String>>(); public InitialResponse getTestBlock(int blockIndex, ClientInfo clientInfo) throws TimeoutException { @@ -216,8 +236,9 @@ */ private StackTraceElement deserialize(StackTraceWrapper stw) { StackTraceElement ste = null; - Object[] args = new Object[] { - stw.className, stw.methodName, stw.fileName, stw.lineNumber}; + + Object[] args = resymbolize(stw); + try { try { // Try the 4-arg ctor (JRE 1.5) @@ -268,4 +289,71 @@ String machine = request.getRemoteHost(); result.setHost(machine); } -} + + private synchronized Map<SymbolName, String> loadSymbolMap( + StrongName strongName) { + Map<SymbolName, String> toReturn = symbolMaps.get(strongName); + if (toReturn != null) { + return toReturn; + } + toReturn = new HashMap<SymbolName, String>(); + + /* + * Collaborate with SymbolMapsLinker for the location of the symbol data + * because the -aux directory isn't accessible via the servlet context. + */ + String path = getRequestModuleBasePath() + "/.junit_symbolMaps/" + + strongName.get() + ".symbolMap"; + InputStream in = getServletContext().getResourceAsStream(path); + if (in == null) { + symbolMaps.put(strongName, null); + return null; + } + + BufferedReader bin = new BufferedReader(new InputStreamReader(in)); + String line; + try { + while ((line = bin.readLine()) != null) { + if (line.charAt(0) == '#') { + continue; + } + int idx = line.indexOf(','); + toReturn.put(new SymbolName(line.substring(0, idx)), + line.substring(idx + 1)); + } + } catch (IOException e) { + toReturn = null; + } + + symbolMaps.put(strongName, toReturn); + return toReturn; + } + + /** + * @return {className, methodName, fileName, lineNumber} + */ + private Object[] resymbolize(StackTraceWrapper stw) { + Object[] toReturn; + StrongName strongName = new StrongName(getPermutationStrongName()); + Map<SymbolName, String> map = loadSymbolMap(strongName); + String symbolData = map == null ? null : map.get(new SymbolName( + stw.methodName)); + + if (symbolData != null) { + // jsniIdent, className, memberName, sourceUri, sourceLine + String[] parts = symbolData.split(","); + assert parts.length == 5 : "Expected 5, have " + parts.length; + + JsniRef ref = JsniRef.parse(parts[0].substring(0, + parts[0].lastIndexOf(')') + 1)); + toReturn = new Object[] { + ref.className(), ref.memberName(), stw.fileName, stw.lineNumber}; + + } else { + // Use the raw data from the client + toReturn = new Object[] { + stw.className, stw.methodName, stw.fileName, stw.lineNumber}; + } + return toReturn; + } +} ======================================= --- /trunk/user/test/com/google/gwt/core/CoreSuite.java Tue Dec 15 13:40:53 2009 +++ /trunk/user/test/com/google/gwt/core/CoreSuite.java Wed Dec 16 17:16:05 2009 @@ -21,7 +21,6 @@ import com.google.gwt.core.client.JsArrayTest; import com.google.gwt.core.client.SchedulerTest; import com.google.gwt.core.client.impl.AsyncFragmentLoaderTest; -import com.google.gwt.core.client.impl.EmulatedStackTraceTest; import com.google.gwt.core.client.impl.SchedulerImplTest; import com.google.gwt.core.client.impl.StackTraceCreatorTest; import com.google.gwt.core.client.impl.XhrLoadingStrategyTest; @@ -38,7 +37,6 @@ // $JUnit-BEGIN$ suite.addTestSuite(AsyncFragmentLoaderTest.class); - suite.addTestSuite(EmulatedStackTraceTest.class); suite.addTestSuite(GWTTest.class); suite.addTestSuite(HttpThrowableReporterTest.class); suite.addTestSuite(JavaScriptExceptionTest.class); ======================================= --- /trunk/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java Mon Oct 26 18:35:41 2009 +++ /trunk/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java Wed Dec 16 17:16:05 2009 @@ -53,7 +53,7 @@ if (!GWT.isScript()) { myName = "testJavaScriptException"; } else { - myName = testJavaScriptExceptionName(); + myName = Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testJavaScriptException()"); } checkStack(myName, t); @@ -97,7 +97,7 @@ if (!GWT.isScript()) { myName = "testStackTraces"; } else { - myName = testStackTracesName(); + myName = Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testStackTraces()"); } checkStack(myName, t); @@ -159,16 +159,6 @@ } } - private static native String testJavaScriptExceptionName() /*-{ - var fn = @com.google.gwt.core.client.impl.StackTraceCreatorTest::testJavaScriptException(); - return @com.google.gwt.core.client.impl.StackTraceCreator::extractNameFromToString(Ljava/lang/String;)(fn.toString()); - }-*/; - - private static native String testStackTracesName() /*-{ - var fn = @com.google.gwt.core.client.impl.StackTraceCreatorTest::testStackTraces(); - return @com.google.gwt.core.client.impl.StackTraceCreator::extractNameFromToString(Ljava/lang/String;)(fn.toString()); - }-*/; - private static native void throwNative() /*-{ null.a(); }-*/; -- http://groups.google.com/group/Google-Web-Toolkit-Contributors