| /** |
| * Copyright (c) 2014 Codetrails GmbH. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Daniel Haftstein - initial tests. |
| */ |
| package org.eclipse.epp.logging.aeri.core.util; |
| |
| import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents; |
| import static org.eclipse.epp.logging.aeri.core.util.Reports.newStatus; |
| import static org.eclipse.epp.logging.aeri.tests.util.TestReports.*; |
| import static org.hamcrest.CoreMatchers.is; |
| import static org.hamcrest.Matchers.*; |
| import static org.junit.Assert.assertThat; |
| |
| import java.lang.reflect.Field; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.epp.logging.aeri.core.IModelFactory; |
| import org.eclipse.epp.logging.aeri.core.IReport; |
| import org.eclipse.epp.logging.aeri.core.IStackTraceElement; |
| import org.eclipse.epp.logging.aeri.core.ISystemSettings; |
| import org.eclipse.epp.logging.aeri.core.IThrowable; |
| import org.eclipse.epp.logging.aeri.tests.util.TestReports; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class ReportsTest { |
| IModelFactory eFac = IModelFactory.eINSTANCE; |
| private static final List<Pattern> PREFIX_WHITELIST = WildcardPatterns |
| .convert(Arrays.asList("sun.*", "java.*", "javax.*", "org.eclipse.*")); |
| |
| private static final String WHITELISTED_CLASSNAME = "java.lang.RuntimeException"; |
| private static final String NOT_WHITELISTED_CLASSNAME = "foo.bar.FooBarException"; |
| |
| private static final String WHITELISTED_CLASSNAME_2 = "java.lang.String"; |
| private static final String WHITELISTED_METHODNAME_2 = "trim"; |
| |
| private static final String NOT_WHITELISTED_CLASSNAME_2 = "foo.bar.AnyClass"; |
| private static final String NOT_WHITELISTED_METHODNAME_2 = "foo"; |
| |
| private static String ANONYMIZED_TAG = "HIDDEN"; |
| |
| private ISystemSettings settings; |
| |
| @Before |
| public void before() { |
| settings = eFac.createSystemSettings(); |
| exCounter = 1; |
| stCounter = 1; |
| } |
| |
| @Test |
| public void testClearEventMessage() { |
| IReport report = createTestReport(); |
| Reports.anonymizeMessages(report); |
| assertThat(report.getStatus().getMessage(), is(ANONYMIZED_TAG)); |
| } |
| |
| @Test |
| public void testDeclaringClassToNull() |
| throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { |
| java.lang.Throwable some = new java.lang.Throwable("contains null"); |
| java.lang.StackTraceElement ste = new java.lang.StackTraceElement("class", "method", null, 1); |
| Field field = java.lang.StackTraceElement.class.getDeclaredField("declaringClass"); |
| field.setAccessible(true); |
| field.set(ste, null); |
| some.setStackTrace(new java.lang.StackTraceElement[] { ste }); |
| IThrowable sut = Reports.newThrowable(some); |
| Assert.assertEquals("MISSING", sut.getStackTrace().get(0).getClassName()); |
| } |
| |
| @Test |
| public void testClearThrowableMessage() { |
| IReport event = createTestReport(); |
| Reports.anonymizeMessages(event); |
| assertThat(event.getStatus().getException().getMessage(), is(ANONYMIZED_TAG)); |
| } |
| |
| @Test |
| public void testAnonymizeThrowableDtoClassname() { |
| IThrowable throwable = createThrowable(NOT_WHITELISTED_CLASSNAME); |
| Reports.anonymizeStackTraces(throwable, PREFIX_WHITELIST); |
| assertThat(throwable.getClassName(), is(ANONYMIZED_TAG)); |
| } |
| |
| @Test |
| public void testAnonymizeThrowableDtoWhitelistedClassname() { |
| IThrowable throwable = createThrowable(WHITELISTED_CLASSNAME); |
| Reports.anonymizeStackTraces(throwable, PREFIX_WHITELIST); |
| assertThat(throwable.getClassName(), is(WHITELISTED_CLASSNAME)); |
| } |
| |
| @Test |
| public void testAnonymizeIStackTraceElementDtoClassnames() { |
| IStackTraceElement element = createStackTraceElement(NOT_WHITELISTED_CLASSNAME_2, NOT_WHITELISTED_METHODNAME_2); |
| Reports.anonymizeStackTraceElements(element, PREFIX_WHITELIST); |
| assertThat(element.getClassName(), is(ANONYMIZED_TAG)); |
| } |
| |
| @Test |
| public void testAnonymizeIStackTraceElementDtoWhitelistedClassnames() { |
| IStackTraceElement element = createStackTraceElement(WHITELISTED_CLASSNAME, ""); |
| Reports.anonymizeStackTraceElements(element, PREFIX_WHITELIST); |
| assertThat(element.getClassName(), is(WHITELISTED_CLASSNAME)); |
| } |
| |
| @Test |
| public void testAnonymizeIStackTraceElementMethodname() { |
| IStackTraceElement element = createStackTraceElement(NOT_WHITELISTED_CLASSNAME_2, NOT_WHITELISTED_METHODNAME_2); |
| Reports.anonymizeStackTraceElements(element, PREFIX_WHITELIST); |
| assertThat(element.getMethodName(), is(ANONYMIZED_TAG)); |
| } |
| |
| @Test |
| public void testAnonymizeIStackTraceElementWhitelistedMethodname() { |
| IStackTraceElement element = createStackTraceElement(WHITELISTED_CLASSNAME_2, WHITELISTED_METHODNAME_2); |
| Reports.anonymizeStackTraceElements(element, PREFIX_WHITELIST); |
| assertThat(element.getMethodName(), is(WHITELISTED_METHODNAME_2)); |
| } |
| |
| @Test |
| public void testFingerprint() { |
| |
| Exception cause = new RuntimeException("cause"); |
| Exception r1 = new RuntimeException("exception message"); |
| |
| r1.fillInStackTrace(); |
| Exception r2 = new RuntimeException("exception message", cause); |
| r2.fillInStackTrace(); |
| |
| IStatus s1 = new Status(IStatus.ERROR, "org.eclipse.epp.logging.aeri", "some error message", r1); |
| IStatus s2 = new Status(IStatus.ERROR, "org.eclipse.epp.logging.aeri", "some error message", r2); |
| |
| org.eclipse.epp.logging.aeri.core.IStatus noCause = Reports.newStatus(s1); |
| org.eclipse.epp.logging.aeri.core.IStatus withCause = Reports.newStatus(s2); |
| |
| Assert.assertNotEquals(noCause.getFingerprint(), withCause.getFingerprint()); |
| } |
| |
| @Test |
| public void testFingerprintNested() { |
| Exception root = new RuntimeException("root"); |
| IStatus s1 = new Status(IStatus.ERROR, "org.eclipse.epp.logging.aeri", "some error message", root); |
| IStatus s2 = new MultiStatus("org.eclipse.epp.logging.aeri", 0, new IStatus[] { s1 }, "some error message", root); |
| |
| org.eclipse.epp.logging.aeri.core.IStatus normal = Reports.newStatus(s1); |
| org.eclipse.epp.logging.aeri.core.IStatus multi = Reports.newStatus(s2); |
| |
| Assert.assertNotEquals(normal.getFingerprint(), multi.getFingerprint()); |
| } |
| |
| @Test |
| public void testCoreExceptionHandling() { |
| IStatus causingStatus = new Status(IStatus.ERROR, "the.causing.plugin", "first message"); |
| java.lang.Throwable causingException = new CoreException(causingStatus); |
| IStatus causedStatus = new Status(IStatus.WARNING, "some.calling.plugin", "any other message", causingException); |
| java.lang.Throwable rootException = new CoreException(causedStatus); |
| IStatus rootEvent = new Status(IStatus.ERROR, "org.eclipse.epp.logging.aeri", "someErrorMessage", rootException); |
| |
| org.eclipse.epp.logging.aeri.core.IStatus rootStatus = Reports.newStatus(rootEvent); |
| |
| org.eclipse.epp.logging.aeri.core.IStatus child = rootStatus.getChildren().get(0); |
| org.eclipse.epp.logging.aeri.core.IStatus leaf = child.getChildren().get(0); |
| assertThat(child.getPluginId(), is("some.calling.plugin")); |
| assertThat(leaf.getPluginId(), is("the.causing.plugin")); |
| } |
| |
| @Test |
| public void testMultistatusDuplicateChildFiltering() { |
| |
| Exception e1 = new Exception("Stack Trace"); |
| e1.setStackTrace(createStacktraceForClasses("java.lang.Object", "org.eclipse.core.internal.jobs.WorkerPool", |
| "org.eclipse.core.internal.jobs.WorkerPool", "org.eclipse.core.internal.jobs.Worker")); |
| IStatus s1 = new Status(IStatus.ERROR, "org.eclipse.ui.monitoring", |
| "Thread 'Worker-3' tid=39 (TIMED_WAITING)\n" + "Waiting for: org.eclipse.core.internal.jobs.WorkerPool@416dc7fc", e1); |
| |
| Exception e2 = new Exception("Stack Trace"); |
| e2.setStackTrace(TestReports.createStacktraceForClasses("java.lang.Object", "org.eclipse.core.internal.jobs.WorkerPool", |
| "org.eclipse.core.internal.jobs.WorkerPool", "org.eclipse.core.internal.jobs.Worker")); |
| IStatus s2 = new Status(IStatus.ERROR, "org.eclipse.ui.monitoring", |
| "Thread 'Worker-2' tid=36 (TIMED_WAITING)\n" + "Waiting for: org.eclipse.core.internal.jobs.WorkerPool@416dc7fc", e2); |
| |
| IStatus multi = new MultiStatus("org.eclipse.ui.monitoring", 0, new IStatus[] { s1, s2 }, "UI freeze of 10s at 08:09:02.936", |
| new RuntimeException("stand-in-stacktrace")); |
| org.eclipse.epp.logging.aeri.core.IStatus newStatus = Reports.newStatus(multi); |
| assertThat(newStatus.getChildren().size(), is(1)); |
| assertThat(newStatus.getMessage(), is("UI freeze of 10s at 08:09:02.936 [1 child-status duplicates removed by Error Reporting]")); |
| } |
| |
| @Test |
| public void testMultistatusChildFilteringHandlesEmptyStacktrace() { |
| |
| Exception e1 = new Exception("Stack Trace 1"); |
| e1.setStackTrace(new java.lang.StackTraceElement[0]); |
| IStatus s1 = new Status(IStatus.ERROR, "org.eclipse.ui.monitoring", "Thread 'Signal Dispatcher' tid=4 (RUNNABLE)", e1); |
| |
| IStatus multi = new MultiStatus("org.eclipse.ui.monitoring", 0, new IStatus[] { s1 }, "UI freeze of 10s at 08:09:02.936", |
| new RuntimeException("stand-in-stacktrace")); |
| org.eclipse.epp.logging.aeri.core.IStatus newStatus = Reports.newStatus(multi); |
| assertThat(newStatus.getChildren().size(), is(0)); |
| } |
| |
| @Test |
| public void testMultistatus() { |
| MultiStatus mst = mst(ex(), st(ex()), st(ex()), st(ex()), st(ex()), st(ex())); |
| org.eclipse.epp.logging.aeri.core.IStatus res = Reports.newStatus(mst); |
| assertThat(numberOfStatusObjects(res), is(6)); |
| } |
| |
| @Test |
| public void testCausedByContainsCoreExceptionWithStatus() { |
| Status st = st(ex(cex(st(ex())))); |
| org.eclipse.epp.logging.aeri.core.IStatus res = newStatus(st); |
| assertThat(numberOfStatusObjects(res), is(2)); |
| } |
| |
| @Test |
| public void testCausedByContainsCoreExceptionWithMultistatus() { |
| MultiStatus mst = mst(ex(), st(ex()), st(ex()), st(ex()), st(ex()), st(ex())); |
| Status st = st(ex(cex(mst))); |
| org.eclipse.epp.logging.aeri.core.IStatus res = Reports.newStatus(st); |
| assertThat(numberOfStatusObjects(res), is(7)); |
| } |
| |
| @Test |
| public void testDetachMultistatusFromCoreException() { |
| // #input |
| // st4 |
| // -ex4 |
| // --cex |
| // ---mst3 |
| // ----ex1 |
| // ----st1 |
| // -----ex2 |
| // ----st2 |
| // -----ex3 |
| Status st = st(ex(cex(mst(ex(), st(ex()), st(ex()))))); |
| // #expected output |
| // st4 |
| // -ex4 |
| // --cex(mst3) |
| // ---ex1 |
| // -mst3 (moved from cex to st4.children) |
| // --ex1 |
| // --st1 |
| // ---ex2 |
| // --st2 |
| // ---ex3 |
| org.eclipse.epp.logging.aeri.core.IStatus st4 = Reports.newStatus(st); |
| IThrowable ex4 = st4.getException(); |
| IThrowable cex = ex4.getCause(); |
| IThrowable ex1 = cex.getCause(); |
| org.eclipse.epp.logging.aeri.core.IStatus mst3 = st4.getChildren().get(0); |
| IThrowable ex1b = mst3.getException(); |
| org.eclipse.epp.logging.aeri.core.IStatus st1 = mst3.getChildren().get(0); |
| IThrowable ex2 = st1.getException(); |
| org.eclipse.epp.logging.aeri.core.IStatus st2 = mst3.getChildren().get(1); |
| IThrowable ex3 = st2.getException(); |
| |
| assertThat(st4.getMessage(), is("st4")); |
| assertThat(ex4.getMessage(), is("ex4")); |
| assertThat(cex.getMessage(), is("mst3")); |
| assertThat(ex1.getMessage(), is("ex1")); |
| assertThat(mst3.getMessage(), is("mst3 [detached from CoreException of Status 'st4' by Error Reporting]")); |
| assertThat(ex1b.getMessage(), is("ex1")); |
| assertThat(st1.getMessage(), is("st1")); |
| assertThat(ex2.getMessage(), is("ex2")); |
| assertThat(st2.getMessage(), is("st2")); |
| assertThat(ex3.getMessage(), is("ex3")); |
| } |
| |
| @Test |
| public void testDetachMultipleMultistatusFromCoreException() { |
| // #input |
| // st9 |
| // -ex6 |
| // --cex |
| // ---st8 |
| // ----cex |
| // -----mst7 |
| // ------ex1 |
| // ------st4 |
| // -------cex |
| // --------mst3 |
| // ---------ex2 |
| // ---------st1 |
| // ----------ex3 |
| // ---------st2 |
| // ----------ex4 |
| // ------st6 |
| // -------cex |
| // --------st5 |
| // ---------ex5 |
| Status st = st(ex(cex(st(cex(mst(ex(), st(cex(mst(ex(), st(ex()), st(ex())))), st(cex(st(ex()))))))))); |
| // #expected output |
| // st9 |
| // -st8 |
| // --mst7 |
| // ---ex1 |
| // ---st4 |
| // ----mst3 |
| // -----ex2 |
| // ----st1 |
| // -----ex3 |
| // ----st2 |
| // -----ex4 |
| // ---st6 |
| // ----st5 |
| // -----ex5 |
| org.eclipse.epp.logging.aeri.core.IStatus st9 = Reports.newStatus(st); |
| IThrowable ex6 = st9.getException(); |
| org.eclipse.epp.logging.aeri.core.IStatus st8 = st9.getChildren().get(0); |
| org.eclipse.epp.logging.aeri.core.IStatus mst7 = st8.getChildren().get(0); |
| org.eclipse.epp.logging.aeri.core.IStatus st4 = mst7.getChildren().get(0); |
| org.eclipse.epp.logging.aeri.core.IStatus mst3 = st4.getChildren().get(0); |
| org.eclipse.epp.logging.aeri.core.IStatus st2 = mst3.getChildren().get(1); |
| IThrowable ex4 = st2.getException(); |
| org.eclipse.epp.logging.aeri.core.IStatus st6 = mst7.getChildren().get(1); |
| org.eclipse.epp.logging.aeri.core.IStatus st5 = st6.getChildren().get(0); |
| IThrowable ex5 = st5.getException(); |
| |
| assertThat(st9.getMessage(), is("st9")); |
| assertThat(ex6.getMessage(), is("ex6")); |
| assertThat(st8.getMessage(), is("st8 [detached from CoreException of Status 'st9' by Error Reporting]")); |
| assertThat(mst7.getMessage(), is("mst7 [detached from CoreException of Status 'st8' by Error Reporting]")); |
| assertThat(st4.getMessage(), is("st4")); |
| assertThat(mst3.getMessage(), is("mst3 [detached from CoreException of Status 'st4' by Error Reporting]")); |
| assertThat(st2.getMessage(), is("st2")); |
| assertThat(ex4.getMessage(), is("ex4")); |
| assertThat(st6.getMessage(), is("st6")); |
| assertThat(st5.getMessage(), is("st5 [detached from CoreException of Status 'st6' by Error Reporting]")); |
| assertThat(ex5.getMessage(), is("ex5")); |
| } |
| |
| @Test |
| public void testPrettyPrintNullSafe1() { |
| IReport report = eFac.createReport(); |
| Reports.toPrettyString(report); |
| } |
| |
| @Test |
| public void testPrettyPrintNullSafe2() { |
| IReport report = eFac.createReport(); |
| report.setStatus(eFac.createStatus()); |
| Reports.toPrettyString(report); |
| } |
| |
| @Test |
| public void testPrettyPrintNullSafe3() { |
| IReport report = eFac.createReport(); |
| report.setStatus(eFac.createStatus()); |
| IThrowable t = eFac.createThrowable(); |
| t.setClassName("org.test"); |
| report.getStatus().setException(t); |
| Reports.toPrettyString(report); |
| } |
| |
| @Test |
| public void testPrettyPrintSkipsNullException() { |
| IReport report = eFac.createReport(); |
| report.setStatus(eFac.createStatus()); |
| String prettyPrint = Reports.toPrettyString(report); |
| assertThat(prettyPrint, not(containsString("Exception"))); |
| } |
| |
| @Test |
| public void testMultistatusMainStacktracesNotFiltered() { |
| |
| Exception e1 = new Exception("Stack Trace"); |
| java.lang.StackTraceElement[] stackTrace = createStacktraceForClasses("java.lang.Thread", |
| "org.eclipse.epp.logging.aeri.ui.actions.UiFreezeAction", "org.eclipse.ui.internal.PluginAction", |
| "org.eclipse.ui.internal.WWinPluginAction", "org.eclipse.jface.action.ActionContributionItem", |
| "org.eclipse.jface.action.ActionContributionItem", "org.eclipse.jface.action.ActionContributionItem", |
| "org.eclipse.swt.widgets.EventTable", "org.eclipse.swt.widgets.Display", "org.eclipse.swt.widgets.Widget", |
| "org.eclipse.swt.widgets.Display", "org.eclipse.swt.widgets.Display", |
| "org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine", "org.eclipse.core.databinding.observable.Realm", |
| "org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine", "org.eclipse.e4.ui.internal.workbench.E4Workbench", |
| "org.eclipse.ui.internal.Workbench", "org.eclipse.core.databinding.observable.Realm", "org.eclipse.ui.internal.Workbench", |
| "org.eclipse.ui.PlatformUI", "org.eclipse.ui.internal.ide.application.IDEApplication", |
| "org.eclipse.equinox.internal.app.EclipseAppHandle", "org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher", |
| "org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher", "org.eclipse.core.runtime.adaptor.EclipseStarter", |
| "org.eclipse.core.runtime.adaptor.EclipseStarter", "sun.reflect.NativeMethodAccessorImpl", |
| "sun.reflect.NativeMethodAccessorImpl", "sun.reflect.DelegatingMethodAccessorImpl", "java.lang.reflect.Method", |
| "org.eclipse.equinox.launcher.Main", "org.eclipse.equinox.launcher.Main", "org.eclipse.equinox.launcher.Main", |
| "org.eclipse.equinox.launcher.Main", "org.eclipse.equinox.launcher.Main"); |
| e1.setStackTrace(stackTrace); |
| IStatus s1 = new Status(IStatus.ERROR, "org.eclipse.ui.monitoring", |
| "Sample at 11:25:04.447 (+1,331s)\n" + "Thread 'main' tid=1 (TIMED_WAITING)", e1); |
| |
| IStatus multi = new MultiStatus("org.eclipse.ui.monitoring", 0, new IStatus[] { s1 }, "UI freeze of 6,0s at 11:24:59.108", |
| new RuntimeException("stand-in-stacktrace")); |
| org.eclipse.epp.logging.aeri.core.IStatus newStatus = Reports.newStatus(multi); |
| assertThat(newStatus.getChildren().size(), is(1)); |
| assertThat(newStatus.getChildren().get(0).getException().getStackTrace().size(), is(stackTrace.length)); |
| } |
| |
| private static int numberOfStatusObjects(org.eclipse.epp.logging.aeri.core.IStatus status) { |
| final AtomicInteger counter = new AtomicInteger(); |
| ModelSwitch<AtomicInteger> visitor = new ModelSwitch<AtomicInteger>() { |
| |
| @Override |
| public AtomicInteger caseStatus(org.eclipse.epp.logging.aeri.core.IStatus object) { |
| counter.incrementAndGet(); |
| return counter; |
| } |
| |
| @Override |
| public AtomicInteger caseThrowable(IThrowable object) { |
| return counter; |
| } |
| }; |
| |
| visitor.doSwitch(status); |
| for (TreeIterator<EObject> it = getAllContents(status, true); it.hasNext();) { |
| visitor.doSwitch(it.next()); |
| } |
| return counter.get(); |
| } |
| |
| private static int exCounter = 1; |
| |
| private static Exception ex() { |
| return ex(null); |
| } |
| |
| private static Exception cex(IStatus st) { |
| return new CoreException(st); |
| } |
| |
| private static Exception ex(Exception cause) { |
| return new Exception("ex" + exCounter++, cause); |
| } |
| |
| private static int stCounter = 1; |
| |
| private static Status st(Exception ex) { |
| return new Status(IStatus.ERROR, "org.eclipse", "st" + stCounter++, ex); |
| } |
| |
| private static MultiStatus mst(Exception ex, Status... status) { |
| return new MultiStatus("org.eclipse", IStatus.ERROR, status, "mst" + stCounter++, ex); |
| } |
| |
| } |