/* * 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.junit.client; import com.google.gwt.core.client.impl.Impl; import com.google.gwt.core.shared.GWT; import com.google.gwt.core.shared.SerializableThrowable; import com.google.gwt.junit.client.WithProperties.Property; import junit.framework.AssertionFailedError; import javaemul.internal.annotations.DoNotInline; /** * This class tests stack traces generated by GWTTestCase. */ public class GWTTestCaseStackTraceTest extends GWTTestCaseTestBase { private static final int LINE_NUMBER_1 = 42; private static final int LINE_NUMBER_2 = LINE_NUMBER_1 + 2; private static final String FILE_NAME = "GWTTestCaseStackTraceTest.java"; private static final String CLASS_NAME = GWTTestCaseStackTraceTest.class.getName(); @DoNotInline private static void throwException(boolean withCause) { // the next line should be LINE_NUMBER_1 AssertionFailedError exception = new AssertionFailedError("stack_trace_msg"); if (withCause) { exception.initCause(new RuntimeException("the_cause")); } throw exception; } private static void assertStackTrace( Throwable t, String methodName, int lineNumber, boolean hasCause) { assertSame(AssertionFailedError.class, t.getClass()); assertTrue(t.getMessage().startsWith("stack_trace_msg")); StackTraceElement[] trace = t.getStackTrace(); assertStackTrace(trace, CLASS_NAME, "throwException", FILE_NAME, LINE_NUMBER_1); assertStackTrace(trace, CLASS_NAME, methodName, FILE_NAME, lineNumber); assertCause(t, hasCause); } private static void assertCause(Throwable t, boolean hasCause) { Throwable cause = t.getCause(); if (hasCause) { assertNotNull(cause); assertCauseDetails(cause); } else { assertNull(cause); } } private static void assertCauseDetails(Throwable t) { assertSame(SerializableThrowable.class, t.getClass()); String type = ((SerializableThrowable) t).getDesignatedType(); assertEquals(RuntimeException.class.getName(), type); assertTrue(t.getMessage().startsWith("the_cause")); StackTraceElement[] trace = t.getStackTrace(); assertStackTrace(trace, CLASS_NAME, "throwException", FILE_NAME, LINE_NUMBER_2); } private static void assertStackTrace(StackTraceElement[] stackTrace, String className, String methodName, String fileName, int lineNumber) { for (StackTraceElement stackTraceElement : stackTrace) { if (stackTraceElement.getClassName().equals(className) && stackTraceElement.getMethodName().equals(methodName)) { assertEquals(fileName, stackTraceElement.getFileName()); assertEquals(lineNumber, stackTraceElement.getLineNumber()); return; // Found!!! } } fail("Stack trace element not found " + className + "#" + methodName); } /** Asserts stack trace generated by {@link #testStackTrace} */ public static class StackTraceAsserter implements ExceptionAsserter { public void assertException(ExpectedFailure annotation, Throwable actual) { final int lineNumber = 101; assertStackTrace(actual, "testStackTrace", lineNumber, false); } } @ExpectedFailure(withAsserter = StackTraceAsserter.class) public void testStackTrace() { throwException(false); } /** Asserts stack trace generated by {@link #testStackTrace_withCause} */ public static class StackTraceAsserterWithCause implements ExceptionAsserter { public void assertException(ExpectedFailure annotation, Throwable actual) { final int lineNumber = 114; assertStackTrace(actual, "testStackTrace_withCause", lineNumber, true); } } @ExpectedFailure(withAsserter = StackTraceAsserterWithCause.class) public void testStackTrace_withCause() { throwException(true); } /** Asserts stack trace generated by {@link #testStackTrace_fromDifferentModule} */ public static class StackTraceAsserterFromDifferentModule implements ExceptionAsserter { public void assertException(ExpectedFailure annotation, Throwable actual) { final int lineNumber = 129; assertStackTrace(actual, "testStackTrace_fromDifferentModule", lineNumber, false); } } // @Property added just to introduce a different module name for the test @WithProperties(@Property(name = "locale", value = "tr")) @ExpectedFailure(withAsserter = StackTraceAsserterFromDifferentModule.class) public void testStackTrace_fromDifferentModule() { throwException(false); } /** Asserts stack trace generated by {@link #testStackTrace_jsni} */ public static class StackTraceAsserterForJsni implements ExceptionAsserter { public void assertException(ExpectedFailure annotation, Throwable actual) { assertEquals(1, actual.getStackTrace().length); StackTraceElement ste = actual.getStackTrace()[0]; assertEquals("setUp", ste.getMethodName()); assertEquals(FILE_NAME, ste.getFileName()); assertEquals(9999, ste.getLineNumber()); } } /** * This is asserting that {@code StacktraceDeobfuscator} trusts the stack trace emulation with the * file name and line number (emulation knows it better for anonymous functions in JSNI). */ @ExpectedFailure(withAsserter = StackTraceAsserterForJsni.class) public void testStackTrace_jsni() { // A method name from a different file so that deobfuscator will resolve to a different file. String methodName = !GWT.isScript() ? "setUp" : Impl.getNameOf("com.google.gwt.junit.client.GWTTestCase::setUp()"); StackTraceElement ste = new StackTraceElement("Unknown", methodName, FILE_NAME, 9999); AssertionFailedError exception = new AssertionFailedError("stack_trace_msg"); exception.setStackTrace(new StackTraceElement[] {ste}); throw exception; } }