
Author: ceki Date: Wed Sep 3 22:28:08 2008 New Revision: 1792 Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/PackageInfo.java - copied, changed from r1790, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/StackTraceElementProxy.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableDataPoint.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToDataPointArray.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToDataPointTest.java Removed: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java Log: LBGENERAL-23 Reworking Throwable to string conversion. Instead of simply converting StackTraceElement (STE) array into just strings, we convert them to a little more sophisticated objects, namely ThrowableDataPoints which support PackageInformation. This is ongoing work, unit test may not pass. Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LRUCache.java Wed Sep 3 22:28:08 2008 @@ -40,6 +40,8 @@ } List<K> keyList() { - return new ArrayList<K>(keySet()); + ArrayList<K> al = new ArrayList<K>(); + al.addAll(keySet()); + return al; } } Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableInformationConverter.java Wed Sep 3 22:28:08 2008 @@ -98,6 +98,7 @@ int length = (lengthOption > stringRep.length) ? stringRep.length : lengthOption; + // an evaluator match will cause stack printing to be skipped if (evaluatorList != null) { boolean printStack = true; for (int i = 0; i < evaluatorList.size(); i++) { Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java ============================================================================== --- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java (original) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java Wed Sep 3 22:28:08 2008 @@ -15,6 +15,8 @@ import org.slf4j.Marker; +import ch.qos.logback.core.helpers.PackageInfo; + /** * * @author James Strachan @@ -77,12 +79,14 @@ * Uses the context class path or the current global class loader to deduce * the file that the given class name comes from */ - static String getJarNameOfClass(String className) { + static String getJarNameOfClass0(String className) { try { Class type = findClass(className); if (type != null) { URL resource = type.getClassLoader().getResource( type.getName().replace('.', '/') + ".class"); + + // "jar:file:/C:/java/../repo/groupId/artifact/1.3/artifact-1.3.jar!/com/some/package/Some.class if (resource != null) { String text = resource.toString(); @@ -107,7 +111,41 @@ } return "na"; } - + + static String getJarNameOfClass1(String className) { + try { + Class type = findClass(className); + if (type != null) { + + + // file:/C:/java/maven-2.0.8/repo/com/icegreen/greenmail/1.3/greenmail-1.3.jar + URL resource = type.getProtectionDomain().getCodeSource().getLocation(); + if (resource != null) { + String text = resource.toString(); + // now lets remove all but the file name + int idx = text.lastIndexOf('/'); + if (idx > 0) { + text = text.substring(idx + 1); + } + idx = text.lastIndexOf('\\'); + if (idx > 0) { + text = text.substring(idx + 1); + } + return text; + } + } + } catch (Exception e) { + // ignore + } + return "na"; + } + + + + static String getJarNameOfClass(String className) { + return getJarNameOfClass1(className); + } + static private Class findClass(String className) { try { return Thread.currentThread().getContextClassLoader() Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LRUCacheTest.java Wed Sep 3 22:28:08 2008 @@ -5,8 +5,6 @@ import java.util.LinkedList; import java.util.List; -import org.junit.After; -import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -15,17 +13,9 @@ public class LRUCacheTest { - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - - } - @Test public void smoke() { + LRUCache<String, String> cache = new LRUCache<String, String>(2); cache.put("a", "a"); cache.put("b", "b"); @@ -38,34 +28,84 @@ @Test public void typicalScenarioTest() { - int simulationLen = 1000 * 20; - int cacheSize = 500; - int worldSize = 10000; + int simulationLen = 1000 * 10; + int cacheSize = 100; + int worldSize = 1000; doScenario(simulationLen, cacheSize, worldSize); } @Test public void scenarioCoverageTest() { - int simulationLen = 1000 * 20; - int[] cacheSizes = new int[] {1,5,10,100,1000,5000,10000}; - int[] worldSizes = new int[] {1,10,100,1000,20000}; + int simulationLen = 1000 * 10; + + int[] cacheSizes = new int[] { 1, 10, 100}; + // tests with large worldSizes are slow because with a large + // world size the probability of a cache miss is high. + int[] worldSizes = new int[] { 1, 10, 100 }; + for (int i = 0; i < cacheSizes.length; i++) { for (int j = 0; j < worldSizes.length; j++) { - System.out.println("cacheSize="+cacheSizes[i]+", worldSize="+worldSizes[j]); doScenario(simulationLen, cacheSizes[i], worldSizes[j]); } } } - void doScenario(int simulationLen, int chacheSize, int worldSize) { - int cacheSize = 500; + void doScenario(int simulationLen, int cacheSize, int worldSize) { int get2PutRatio = 10; - - Simulator simulator = new Simulator(worldSize, get2PutRatio); + Simulator simulator = new Simulator(worldSize, get2PutRatio, false); List<Event> scenario = simulator.generateScenario(simulationLen); LRUCache<String, String> lruCache = new LRUCache<String, String>(cacheSize); T_LRUCache<String> tlruCache = new T_LRUCache<String>(cacheSize); + long start = System.nanoTime(); simulator.simulate(scenario, lruCache, tlruCache); - assertEquals(tlruCache.ketList(), lruCache.keyList()); + //assertEquals(tlruCache.keyList(), lruCache.keyList()); + long end = System.nanoTime(); + System.out.println("cacheSize=" + cacheSize + ", worldSize=" + worldSize + + ", elapsed time=" + ((end - start) / (1000 * 1000)) + " in millis"); + } + + + + @Test + @Ignore // slow test that is known to pass + public void multiThreadedScenario() throws InterruptedException { + int cacheSize = 100; + int worldSize = cacheSize*2; + LRUCache<String, String> lruCache = new LRUCache<String, String>(cacheSize); + T_LRUCache<String> tlruCache = new T_LRUCache<String>(cacheSize); + SimulatorRunnable[] simulatorArray = new SimulatorRunnable[5]; + for(int i = 0; i < simulatorArray.length; i++) { + simulatorArray[i] = new SimulatorRunnable(lruCache, tlruCache, worldSize); + } + for(int i = 0; i < simulatorArray.length; i++) { + simulatorArray[i].start(); + } + for(int i = 0; i < simulatorArray.length; i++) { + simulatorArray[i].join(); + } + assertEquals(tlruCache.keyList(), lruCache.keyList()); + } + + private class SimulatorRunnable extends Thread { + + LRUCache<String, String> lruCache; + T_LRUCache<String> tlruCache; + int worldSize; + + SimulatorRunnable(LRUCache<String, String> lruCache, T_LRUCache<String> tlruCache, int worldSize) { + this.lruCache = lruCache; + this.tlruCache = tlruCache; + this.worldSize = worldSize; + } + + public void run() { + int get2PutRatio = 10; + int simulationLen = 1000*50; + Simulator simulator = new Simulator(worldSize, get2PutRatio, true); + List<Event> scenario = simulator.generateScenario(simulationLen); + simulator.simulate(scenario, lruCache, tlruCache); + System.out.println("done"); + } } + } Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/Simulator.java Wed Sep 3 22:28:08 2008 @@ -11,28 +11,29 @@ public class Simulator { - Random random; - + int worldSize; int get2PutRatio; - - public Simulator(int worldSize, int get2PutRatio) { + boolean multiThreaded; + + public Simulator(int worldSize, int get2PutRatio, boolean multiThreaded) { this.worldSize = worldSize; this.get2PutRatio = get2PutRatio; long seed = System.nanoTime(); - System.out.println("seed is "+seed); + // System.out.println("seed is "+seed); random = new Random(seed); + this.multiThreaded = multiThreaded; } - + public List<Event> generateScenario(int len) { List<Event> scenario = new ArrayList<Event>(); - - for(int i = 0; i < len; i++) { - + + for (int i = 0; i < len; i++) { + int r = random.nextInt(get2PutRatio); boolean put = false; - if(r == 0) { + if (r == 0) { put = true; } r = random.nextInt(worldSize); @@ -41,30 +42,40 @@ } return scenario; } - - public void simulate(List<Event> scenario, LRUCache<String, String> lruCache, T_LRUCache<String> tlruCache) { - for(Event<String> e: scenario) { - if(e.put) { + + public void simulate(List<Event> scenario, LRUCache<String, String> lruCache, + T_LRUCache<String> tlruCache) { + for (Event<String> e : scenario) { + if (e.put) { lruCache.put(e.k, e.k); tlruCache.put(e.k); } else { + @SuppressWarnings("unused") String r0 = lruCache.get(e.k); + @SuppressWarnings("unused") String r1 = tlruCache.get(e.k); - if(r0 != null) { - assertEquals(r0, e.k); + if (!multiThreaded) { + // if the simulation is used in a multi-threaded + // context, then the state of lruCache may be different than + // that of tlruCache. In single threaded mode, they should + // return the same values all the time + if (r0 != null) { + assertEquals(r0, e.k); + } + assertEquals(r0, r1); } - assertEquals(r0, r1); } } } - -// void compareAndDumpIfDifferent(LRUCache<String, String> lruCache, T_LRUCache<String> tlruCache) { -// lruCache.dump(); -// tlruCache.dump(); -// if(!lruCache.keyList().equals(tlruCache.ketList())) { -// lruCache.dump(); -// tlruCache.dump(); -// throw new AssertionFailedError("s"); -// } -// } + + // void compareAndDumpIfDifferent(LRUCache<String, String> lruCache, + // T_LRUCache<String> tlruCache) { + // lruCache.dump(); + // tlruCache.dump(); + // if(!lruCache.keyList().equals(tlruCache.ketList())) { + // lruCache.dump(); + // tlruCache.dump(); + // throw new AssertionFailedError("s"); + // } + // } } Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/UtilTest.java Wed Sep 3 22:28:08 2008 @@ -4,9 +4,10 @@ import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; +import ch.qos.logback.core.helpers.PackageInfo; + import com.icegreen.greenmail.util.GreenMail; import com.icegreen.greenmail.util.ServerSetup; @@ -37,7 +38,7 @@ PackageInfo pi = Util.getPackageInfo(className); System.out.println(" at " + className + "." + ste.getMethodName() + "(" + ste.getFileName() + ":" + ste.getLineNumber() + ") [" - + pi.jarName + ":" + pi.version + "]"); + + pi.getJarName() + ":" + pi.getVersion() + "]"); } } } @@ -68,17 +69,16 @@ } @Test - @Ignore public void perfTest() { int len = 1000; loop(len, false); double d0 = loop(len, false); - System.out.println("false " + d0); + System.out.println("ve=false " + d0); loop(len, true); double d1 = loop(len, true); - System.out.println("false " + d1); + System.out.println("ve=true " + d1); } } Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/lru/T_LRUCache.java Wed Sep 3 22:28:08 2008 @@ -31,7 +31,7 @@ } @SuppressWarnings("unchecked") - public void put(K k) { + synchronized public void put(K k) { sequenceNumber++; T_Entry<K> te = getEntry(k); if (te != null) { @@ -47,7 +47,7 @@ } @SuppressWarnings("unchecked") - public K get(K k) { + synchronized public K get(K k) { T_Entry<K> te = getEntry(k); if (te == null) { return null; @@ -58,7 +58,7 @@ } } - public List<K> ketList() { + synchronized public List<K> keyList() { List<K> keyList = new ArrayList<K>(); for (T_Entry<K> e : cacheList) { keyList.add(e.k); @@ -86,3 +86,4 @@ } } + Copied: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/PackageInfo.java (from r1790, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java) ============================================================================== --- /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PackageInfo.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/PackageInfo.java Wed Sep 3 22:28:08 2008 @@ -1,13 +1,29 @@ -package ch.qos.logback.classic.pattern; +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.core.helpers; public class PackageInfo { - String jarName; String version; - PackageInfo(String jarName, String version) { + public PackageInfo(String jarName, String version) { this.jarName = jarName; this.version = version; } + + public String getJarName() { + return jarName; + } + + public String getVersion() { + return version; + } } Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/StackTraceElementProxy.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/StackTraceElementProxy.java Wed Sep 3 22:28:08 2008 @@ -0,0 +1,25 @@ +package ch.qos.logback.core.helpers; + +public class StackTraceElementProxy { + + final StackTraceElement ste; + private String steAsString; + private PackageInfo pi; + + StackTraceElementProxy(StackTraceElement ste) { + this.ste = ste; + } + + public String getSTEAsString() { + if(steAsString == null) { + steAsString = "\tat "+ste.toString(); + } + return steAsString; + } + + public PackageInfo getPI() { + // compute pi from ste + return pi; + } + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableDataPoint.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableDataPoint.java Wed Sep 3 22:28:08 2008 @@ -0,0 +1,45 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ +package ch.qos.logback.core.helpers; + +public class ThrowableDataPoint { + + enum ThrowableDataPointType { + RAW, STEP; + } + + String rawString; + StackTraceElementProxy step; + final ThrowableDataPointType type; + + ThrowableDataPoint(String rawString) { + this.rawString = rawString; + this.type = ThrowableDataPointType.RAW; + } + + ThrowableDataPoint(StackTraceElement ste) { + this.step = new StackTraceElementProxy(ste); + this.type = ThrowableDataPointType.STEP; + } + + public ThrowableDataPointType getType() { + return type; + } + + @Override + public String toString() { + switch(type) { + case RAW: return rawString; + case STEP: return step.getSTEAsString(); + } + throw new IllegalStateException("Unreachable code"); + } + +} Added: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToDataPointArray.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToDataPointArray.java Wed Sep 3 22:28:08 2008 @@ -0,0 +1,87 @@ +/** + * Logback: the generic, reliable, fast and flexible logging framework. + * + * Copyright (C) 2000-2008, QOS.ch + * + * This library is free software, you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation. + */ + +package ch.qos.logback.core.helpers; + +import java.util.LinkedList; +import java.util.List; + +import ch.qos.logback.core.CoreGlobal; + +public class ThrowableToDataPointArray { + + static final ThrowableDataPoint[] TEMPLATE_ARRAY = new ThrowableDataPoint[0]; + + public static ThrowableDataPoint[] convert(Throwable t) { + List<ThrowableDataPoint> tdpList = new LinkedList<ThrowableDataPoint>(); + extract(tdpList, t, null); + return tdpList.toArray(TEMPLATE_ARRAY); + } + + private static void extract(List<ThrowableDataPoint> tdpList, Throwable t, + StackTraceElement[] parentSTE) { + StackTraceElement[] ste = t.getStackTrace(); + final int numberOfcommonFrames = findNumberOfCommonFrames(ste, parentSTE); + + tdpList.add(firstLineToDataPoint(t, parentSTE)); + for (int i = 0; i < (ste.length - numberOfcommonFrames); i++) { + tdpList.add(new ThrowableDataPoint(ste[i])); + } + + // buf.append("\tat "); + + + if (numberOfcommonFrames != 0) { + tdpList.add(new ThrowableDataPoint("\t... "+numberOfcommonFrames + + " common frames omitted")); + } + + Throwable cause = t.getCause(); + if (cause != null) { + extract(tdpList, cause, ste); + } + } + + private static ThrowableDataPoint firstLineToDataPoint(Throwable t, + StackTraceElement[] parentSTE) { + String prefix = ""; + if (parentSTE != null) { + prefix = CoreGlobal.CAUSED_BY; + } + + String result = prefix + t.getClass().getName(); + if (t.getMessage() != null) { + result += ": " + t.getMessage(); + } + return new ThrowableDataPoint(result); + } + + private static int findNumberOfCommonFrames(StackTraceElement[] ste, + StackTraceElement[] parentSTE) { + if (parentSTE == null) { + return 0; + } + + int steIndex = ste.length - 1; + int parentIndex = parentSTE.length - 1; + int count = 0; + while (steIndex >= 0 && parentIndex >= 0) { + if (ste[steIndex].equals(parentSTE[parentIndex])) { + count++; + } else { + break; + } + steIndex--; + parentIndex--; + } + return count; + } + +} Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java Wed Sep 3 22:28:08 2008 @@ -18,31 +18,22 @@ String[] result; StackTraceElement[] ste = t.getStackTrace(); - final int commonFrames = findCommonFrames(ste, parentSTE); + final int numberOfcommonFrames = findNumberOfCommonFrames(ste, parentSTE); final String[] firstArray; - if (commonFrames == 0) { + if (numberOfcommonFrames == 0) { firstArray = new String[ste.length + 1]; } else { - firstArray = new String[ste.length - commonFrames + 2]; + firstArray = new String[ste.length - numberOfcommonFrames + 2]; } - String prefix = ""; - if (parentSTE != null) { - prefix = CoreGlobal.CAUSED_BY; - } - - firstArray[0] = prefix + t.getClass().getName(); - if (t.getMessage() != null) { - firstArray[0] += ": " + t.getMessage(); - } - - for (int i = 0; i < (ste.length - commonFrames); i++) { + firstArray[0] = formatFirstLine(t, parentSTE); + for (int i = 0; i < (ste.length - numberOfcommonFrames); i++) { firstArray[i + 1] = ste[i].toString(); } - if (commonFrames != 0) { - firstArray[firstArray.length - 1] = commonFrames + if (numberOfcommonFrames != 0) { + firstArray[firstArray.length - 1] = numberOfcommonFrames + " common frames omitted"; } @@ -60,7 +51,20 @@ return result; } - private static int findCommonFrames(StackTraceElement[] ste, + private static String formatFirstLine(Throwable t, StackTraceElement[] parentSTE) { + String prefix = ""; + if (parentSTE != null) { + prefix = CoreGlobal.CAUSED_BY; + } + + String result = prefix + t.getClass().getName(); + if (t.getMessage() != null) { + result += ": " + t.getMessage(); + } + return result; + } + + private static int findNumberOfCommonFrames(StackTraceElement[] ste, StackTraceElement[] parentSTE) { if (parentSTE == null) { return 0; Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToDataPointTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToDataPointTest.java Wed Sep 3 22:28:08 2008 @@ -0,0 +1,81 @@ +package ch.qos.logback.core.helpers; + +import static org.junit.Assert.*; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.qos.logback.core.Layout; + +public class ThrowableToDataPointTest { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + public void verify(Throwable t) { + t.printStackTrace(pw); + + ThrowableDataPoint[] tdpArray = ThrowableToDataPointArray.convert(t); + StringBuilder sb = new StringBuilder(); + for (ThrowableDataPoint tdp : tdpArray) { + sb.append(tdp.toString()); + sb.append(Layout.LINE_SEP); + } + String expected = sw.toString(); + String result = sb.toString().replace("common frames omitted", "more"); + + assertEquals(expected, result); + } + + @Test + public void smoke() { + Exception e = new Exception("smoke"); + verify(e); + } + + @Test + public void nested() { + Exception w = null; + try { + someMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + @Test + public void multiNested() { + Exception w = null; + try { + someOtherMethod(); + } catch (Exception e) { + w = new Exception("wrapping", e); + } + verify(w); + } + + void someMethod() throws Exception { + throw new Exception("someMethod"); + } + + void someOtherMethod() throws Exception { + try { + someMethod(); + } catch (Exception e) { + throw new Exception("someOtherMethod", e); + } + } +}