
Author: ceki Date: Thu Dec 18 21:11:24 2008 New Revision: 2084 Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java logback/trunk/logback-classic/src/test/input/joran/hoard/completeCycle.xml logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java logback/trunk/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java Log: - added DuplicateMessageFilter - BasicStatusMessages's internal buffer now has two parts, the head part and the tail part. Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java Thu Dec 18 21:11:24 2008 @@ -0,0 +1,68 @@ +/** + * 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.classic.turbo; + +import org.slf4j.Marker; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.core.spi.FilterReply; + +public class DuplicateMessageFilter extends TurboFilter { + + static final int DEFAULT_CACHE_SIZE = 100; + static final int DEFAULT_ALLOWED_REPETITIONS = 5; + + public int allowedRepetitions = DEFAULT_ALLOWED_REPETITIONS; + public int cacheSize = DEFAULT_CACHE_SIZE; + + private LRUMessageCache msgCache; + + @Override + public void start() { + msgCache = new LRUMessageCache(cacheSize); + super.start(); + } + + @Override + public void stop() { + msgCache.clear(); + msgCache = null; + super.stop(); + } + + @Override + public FilterReply decide(Marker marker, Logger logger, Level level, + String format, Object[] params, Throwable t) { + int count = msgCache.getMessageCount(format); + if(count <= allowedRepetitions) { + return FilterReply.NEUTRAL; + } else { + return FilterReply.DENY; + } + } + + public int getAllowedRepetitions() { + return allowedRepetitions; + } + + public void setAllowedRepetitions(int allowedRepetitions) { + this.allowedRepetitions = allowedRepetitions; + } + + public int getCacheSize() { + return cacheSize; + } + + public void setCacheSize(int cacheSize) { + this.cacheSize = cacheSize; + } + +} Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java Thu Dec 18 21:11:24 2008 @@ -0,0 +1,43 @@ +/** + * 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.classic.turbo; + +import java.util.LinkedHashMap; +import java.util.Map; + +class LRUMessageCache extends LinkedHashMap<String, Integer> { + + private static final long serialVersionUID = 1L; + + final int cacheSize; + + LRUMessageCache(int cacheSize) { + super((int) (cacheSize * (4.0f / 3)), 0.75f, true); + if (cacheSize < 1) { + throw new IllegalArgumentException("Cache size cannnot be smaller than 1"); + } + this.cacheSize = cacheSize; + } + + int getMessageCount(String msg) { + Integer i = super.get(msg); + if(i == null) { + i = 1; + } else { + i = new Integer(i.intValue()+1); + } + super.put(msg, i); + return i; + } + + protected boolean removeEldestEntry(Map.Entry eldest) { + return (size() > cacheSize); + } +} Added: logback/trunk/logback-classic/src/test/input/joran/hoard/completeCycle.xml ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/input/joran/hoard/completeCycle.xml Thu Dec 18 21:11:24 2008 @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE configuration> + +<configuration debug="true"> + + <appender name="HOARD" + class="ch.qos.logback.classic.hoard.HoardingAppender"> + + <mdcKey>cycle</mdcKey> + <defaultValue>cycleDefault</defaultValue> + <hoard> + <appender name="list-${cycle}" class="ch.qos.logback.core.read.ListAppender"/> + </hoard> + </appender> + + <root level="DEBUG"> + <appender-ref ref="HOARD" /> + </root> + +</configuration> Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java Thu Dec 18 21:11:24 2008 @@ -33,7 +33,7 @@ suite.addTest(ch.qos.logback.classic.pattern.PackageTest.suite()); suite.addTest(ch.qos.logback.classic.db.PackageTest.suite()); suite.addTest(ch.qos.logback.classic.spi.PackageTest.suite()); - suite.addTest(ch.qos.logback.classic.turbo.PackageTest.suite()); + suite.addTest(new JUnit4TestAdapter(ch.qos.logback.classic.turbo.PackageTest.class)); suite.addTest(new JUnit4TestAdapter( ch.qos.logback.classic.hoard.PackageTest.class)); Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/AppenderTrackerTest.java Thu Dec 18 21:11:24 2008 @@ -24,9 +24,17 @@ la.setContext(context); la.start(); } + @Test - public void empty() { + public void empty0() { + long now = 3000; + appenderTracker.stopStaleAppenders(now); + assertEquals(0, appenderTracker.keyList().size()); + } + + @Test + public void empty1() { long now = 3000; assertNull(appenderTracker.get("a", now++)); now += AppenderTrackerImpl.THRESHOLD+1000; Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/hoard/HoardingAppenderTest.java Thu Dec 18 21:11:24 2008 @@ -16,7 +16,9 @@ import java.util.List; import org.junit.Test; +import org.slf4j.MDC; +import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; @@ -56,8 +58,8 @@ long timestamp = 0; HoardingAppender ha = (HoardingAppender) root.getAppender("HOARD"); ListAppender<LoggingEvent> listAppender = (ListAppender<LoggingEvent>) ha.appenderTracker.get("smoke", timestamp); - StatusPrinter.print(loggerContext); + StatusPrinter.print(loggerContext); assertNotNull(listAppender); List<LoggingEvent> eventList = listAppender.list; assertEquals(1, listAppender.list.size()); @@ -65,11 +67,28 @@ } @Test - public void testLevel() throws JoranException { - configure(PREFIX + "hoard0.xml"); - logger.debug("ss"); - //StatusPrinter.print(loggerContext); + public void testWholeCycle() throws JoranException { + String mdcKey = "cycle"; + configure(PREFIX + "completeCycle.xml"); + MDC.put(mdcKey, "a"); + logger.debug("smoke"); + long timestamp = System.currentTimeMillis(); + HoardingAppender ha = (HoardingAppender) root.getAppender("HOARD"); + ListAppender<LoggingEvent> listAppender = (ListAppender<LoggingEvent>) ha.appenderTracker.get("a", timestamp); + StatusPrinter.print(loggerContext); - } + assertNotNull(listAppender); + List<LoggingEvent> eventList = listAppender.list; + assertEquals(1, listAppender.list.size()); + assertEquals("smoke", eventList.get(0).getMessage()); + MDC.remove(mdcKey); + LoggingEvent le = new LoggingEvent("x", logger, Level.INFO, "hello", null, null); + le.setTimeStamp(timestamp+AppenderTracker.THRESHOLD*2); + ha.doAppend(le); + assertFalse(listAppender.isStarted()); + assertEquals(1, ha.appenderTracker.keyList().size()); + assertEquals("cycleDefault", ha.appenderTracker.keyList().get(0)); + + } } Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java Thu Dec 18 21:11:24 2008 @@ -0,0 +1,54 @@ +package ch.qos.logback.classic.turbo; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import ch.qos.logback.core.spi.FilterReply; + + +public class DuplicateMessageFilterTest { + + + @Test + public void smoke() { + DuplicateMessageFilter dmf = new DuplicateMessageFilter(); + dmf.setAllowedRepetitions(1); + dmf.start(); + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "x", null, null)); + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "y", null, null)); + assertEquals(FilterReply.DENY, dmf.decide(null, null, null, "x", null, null)); + assertEquals(FilterReply.DENY, dmf.decide(null, null, null, "y", null, null)); + } + + @Test + public void memoryLoss() { + DuplicateMessageFilter dmf = new DuplicateMessageFilter(); + dmf.setAllowedRepetitions(1); + dmf.setCacheSize(1); + dmf.start(); + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "a", null, null)); + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "b", null, null)); + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "a", null, null)); + } + + + @Test + public void many() { + DuplicateMessageFilter dmf = new DuplicateMessageFilter(); + dmf.setAllowedRepetitions(1); + int cacheSize = 10; + int margin = 2; + dmf.setCacheSize(cacheSize); + dmf.start(); + for(int i = 0; i < cacheSize+margin; i++) { + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "a"+i, null, null)); + } + for(int i = cacheSize-1; i >= margin; i--) { + assertEquals(FilterReply.DENY, dmf.decide(null, null, null, "a"+i, null, null)); + } + for(int i = margin-1; i >= 0; i--) { + assertEquals(FilterReply.NEUTRAL, dmf.decide(null, null, null, "a"+i, null, null)); + } + } +} Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java Thu Dec 18 21:11:24 2008 @@ -1,30 +1,23 @@ package ch.qos.logback.classic.turbo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import ch.qos.logback.core.spi.FilterReply; -import junit.framework.TestCase; - -public class MarkerFilterTest extends TestCase { +public class MarkerFilterTest { static String MARKER_NAME = "toto"; Marker totoMarker = MarkerFactory.getMarker(MARKER_NAME); - public MarkerFilterTest(String arg0) { - super(arg0); - } - - protected void setUp() throws Exception { - super.setUp(); - } - - protected void tearDown() throws Exception { - super.tearDown(); - } + @Test public void testNoMarker() { MarkerFilter mkt = new MarkerFilter(); mkt.start(); @@ -33,7 +26,9 @@ assertEquals(FilterReply.NEUTRAL, mkt.decide(null, null, null, null, null, null)); } - + + + @Test public void testBasic() { MarkerFilter mkt = new MarkerFilter(); mkt.setMarker(MARKER_NAME); Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java ============================================================================== --- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java (original) +++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java Thu Dec 18 21:11:24 2008 @@ -10,15 +10,11 @@ package ch.qos.logback.classic.turbo; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; -public class PackageTest extends TestCase { - - public static Test suite() { - TestSuite suite = new TestSuite(); - suite.addTestSuite(MarkerFilterTest.class); - return suite; - } +@RunWith(Suite.class) +@SuiteClasses( { MarkerFilterTest.class, DuplicateMessageFilterTest.class}) +public class PackageTest { } \ No newline at end of file Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java Thu Dec 18 21:11:24 2008 @@ -12,18 +12,22 @@ import java.util.ArrayList; import java.util.List; +import ch.qos.logback.core.helpers.CyclicBuffer; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.status.StatusManager; public class BasicStatusManager implements StatusManager { - public static final int MAX_COUNT = 200; + public static final int MAX_HEADER_COUNT = 150; + public static final int TAIL_SIZE = 150; int count = 0; // protected access was requested in http://jira.qos.ch/browse/LBCORE-36 final protected List<Status> statusList = new ArrayList<Status>(); + final protected CyclicBuffer<Status> tailBuffer = new CyclicBuffer<Status>( + TAIL_SIZE); final protected Object statusListLock = new Object(); int level = Status.INFO; @@ -34,7 +38,7 @@ // Note on synchronization // This class contains two separate locks statusListLock and - // statusListenerListLock guarding respectively the statusList and + // statusListenerListLock guarding respectively the statusList+tailBuffer and // statusListenerList fields. The locks are used internally // without cycles. They are exposed to derived classes which should be careful // not to create deadlock cycles. @@ -49,21 +53,28 @@ // LBCORE-72: fire event before the count check fireStatusAddEvent(newStatus); - if (count > MAX_COUNT) { - return; - } count++; - if (newStatus.getLevel() > level) { level = newStatus.getLevel(); } synchronized (statusListLock) { - statusList.add(newStatus); + if (statusList.size() < MAX_HEADER_COUNT) { + statusList.add(newStatus); + } else { + tailBuffer.add(newStatus); + } } } - + public List<Status> getCopyOfStatusList() { + synchronized (statusListLock) { + List<Status> tList = new ArrayList<Status>(statusList); + tList.addAll(tailBuffer.asList()); + return tList; + } + } + private void fireStatusAddEvent(Status status) { synchronized (statusListenerListLock) { for (StatusListener sl : statusListenerList) { @@ -72,12 +83,6 @@ } } - public List<Status> getCopyOfStatusList() { - synchronized (statusListLock) { - return new ArrayList<Status>(statusList); - } - } - public int getLevel() { return level; } Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java Thu Dec 18 21:11:24 2008 @@ -10,14 +10,14 @@ package ch.qos.logback.core.helpers; +import java.util.ArrayList; +import java.util.List; /** + * CyclicBuffer holds values in a cyclic array. * - * CyclicBuffer is used by other appenders to hold - * objects for immediate or differed display. - * <p> - * This buffer gives read access to any element in the buffer not just the first - * or last element. + * <p>It allows read access to any element in the buffer not just the first or + * last element. * * @author Ceki Gülcü */ @@ -35,7 +35,7 @@ * The <code>maxSize</code> argument must a positive integer. * * @param maxSize - * The maximum number of elements in the buffer. + * The maximum number of elements in the buffer. */ @SuppressWarnings("unchecked") public CyclicBuffer(int maxSize) throws IllegalArgumentException { @@ -45,7 +45,7 @@ } init(maxSize); } - + @SuppressWarnings("unchecked") private void init(int maxSize) { this.maxSize = maxSize; @@ -54,10 +54,9 @@ last = 0; numElems = 0; } - + /** - * Clears the buffer - * and resets all attributes. + * Clears the buffer and resets all attributes. */ public void clear() { init(this.maxSize); @@ -94,7 +93,6 @@ return maxSize; } - /** * Get the oldest (first) element in the buffer. The oldest element is removed * from the buffer. @@ -110,6 +108,14 @@ } return r; } + + public List<E> asList() { + List<E> tList = new ArrayList<E>(); + for(int i = 0; i < length(); i++) { + tList.add(get(i)); + } + return tList; + } /** * Get the number of elements in the buffer. This number is guaranteed to be @@ -123,7 +129,7 @@ * Resize the cyclic buffer to <code>newSize</code>. * * @throws IllegalArgumentException - * if <code>newSize</code> is negative. + * if <code>newSize</code> is negative. */ @SuppressWarnings("unchecked") public void resize(int newSize) { Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java Thu Dec 18 21:11:24 2008 @@ -52,7 +52,7 @@ appender.setContext(context); - String appenderName = attributes.getValue(NAME_ATTRIBUTE); + String appenderName = ec.subst(attributes.getValue(NAME_ATTRIBUTE)); if (OptionHelper.isEmpty(appenderName)) { addWarn("No appender name given for appender of type " + className Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java Thu Dec 18 21:11:24 2008 @@ -1,11 +1,11 @@ /** - * LOGBack: the generic, reliable, fast and flexible logging framework. - * - * Copyright (C) 1999-2006, 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. + * 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.read; Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java ============================================================================== --- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java (original) +++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java Thu Dec 18 21:11:24 2008 @@ -139,4 +139,33 @@ return buf.toString(); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + level; + result = prime * result + ((message == null) ? 0 : message.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + final StatusBase other = (StatusBase) obj; + if (level != other.level) + return false; + if (message == null) { + if (other.message != null) + return false; + } else if (!message.equals(other.message)) + return false; + return true; + } + + } Modified: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java ============================================================================== --- logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java (original) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java Thu Dec 18 21:11:24 2008 @@ -14,7 +14,8 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ch.qos.logback.core.util.PackageTest.class, +@SuiteClasses({BasicStatusManagerTest.class, + ch.qos.logback.core.util.PackageTest.class, ch.qos.logback.core.helpers.PackageTest.class, ch.qos.logback.core.pattern.PackageTest.class, ch.qos.logback.core.PackageTest.class, Added: logback/trunk/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java ============================================================================== --- (empty file) +++ logback/trunk/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java Thu Dec 18 21:11:24 2008 @@ -0,0 +1,53 @@ +package ch.qos.logback.core; + +import static ch.qos.logback.core.BasicStatusManager.MAX_HEADER_COUNT; +import static ch.qos.logback.core.BasicStatusManager.TAIL_SIZE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import ch.qos.logback.core.status.ErrorStatus; +import ch.qos.logback.core.status.Status; + + +public class BasicStatusManagerTest { + + + BasicStatusManager bsm = new BasicStatusManager(); + + @Test + public void smoke() { + bsm.add(new ErrorStatus("hello", this)); + assertEquals(Status.ERROR, bsm.getLevel()); + + List<Status> statusList = bsm.getCopyOfStatusList(); + assertNotNull(statusList); + assertEquals(1, statusList.size()); + assertEquals("hello", statusList.get(0).getMessage()); + } + + @Test + public void many() { + int margin = 300; + int len = MAX_HEADER_COUNT+TAIL_SIZE+margin; + for(int i = 0; i < len; i++) { + bsm.add(new ErrorStatus(""+i, this)); + } + + List<Status> statusList = bsm.getCopyOfStatusList(); + assertNotNull(statusList); + assertEquals(MAX_HEADER_COUNT+TAIL_SIZE, statusList.size()); + List<Status> witness = new ArrayList<Status>(); + for(int i = 0; i < MAX_HEADER_COUNT; i++) { + witness.add(new ErrorStatus(""+i, this)); + } + for(int i = 0; i < TAIL_SIZE; i++) { + witness.add(new ErrorStatus(""+(MAX_HEADER_COUNT+margin+i), this)); + } + assertEquals(witness, statusList); + } +}