Author: carnold
Date: Wed Mar 15 08:45:59 2006
New Revision: 386111
URL: http://svn.apache.org/viewcvs?rev=386111&view=rev
Log:
Bug 38982: AsyncAppender unit tests with 100% coverage of existing code
Modified:
logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
Modified:
logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
URL:
http://svn.apache.org/viewcvs/logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java?rev=386111&r1=386110&r2=386111&view=diff
==============================================================================
---
logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
(original)
+++
logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
Wed Mar 15 08:45:59 2006
@@ -1,12 +1,12 @@
/*
- * Copyright 1999,2004 The Apache Software Foundation.
- *
+ * Copyright 1999,2006 The Apache Software Foundation.
+ *
* 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.
@@ -16,121 +16,550 @@
package org.apache.log4j;
-import java.util.Vector;
-
import junit.framework.TestCase;
import org.apache.log4j.spi.LoggingEvent;
+import java.util.Enumeration;
+import java.util.Vector;
+
/**
- A superficial but general test of log4j.
+ * Tests for AsyncAppender.
+ *
*/
-public class AsyncAppenderTestCase extends TestCase {
- static int DELAY = 10;
-
- Logger root = Logger.getRootLogger();;
- Layout layout = new SimpleLayout();;
- VectorAppender vectorAppender;
- AsyncAppender asyncAppender = new AsyncAppender();
-
- public AsyncAppenderTestCase(String name) {
+public final class AsyncAppenderTestCase extends TestCase {
+ /**
+ * root logger.
+ */
+ private final Logger root = Logger.getRootLogger();
+
+ /**
+ * appender under test.
+ */
+ private AsyncAppender asyncAppender;
+
+ /**
+ * Create new instance of test.
+ * @param name test name.
+ */
+ public AsyncAppenderTestCase(final String name) {
super(name);
}
- public void setUp() {
- vectorAppender = new VectorAppender();
- vectorAppender.setDelay(DELAY);
- asyncAppender.addAppender(vectorAppender);
- asyncAppender.activateOptions();
- root.addAppender(asyncAppender);
+ /**
+ * Create a vector appender with a 10 ms delay.
+ * @return new VectorAppender.
+ */
+ private static VectorAppender createDelayedAppender() {
+ VectorAppender vectorAppender = new VectorAppender();
+ vectorAppender.setDelay(10);
+
+ return vectorAppender;
+ }
+
+ /**
+ * Create new appender and attach to root logger.
+ * @param wrappedAppender appender wrapped by async logger.
+ * @param bufferSize buffer size.
+ * @return new AsyncAppender.
+ */
+ private static AsyncAppender createAsyncAppender(
+ final Appender wrappedAppender, final int bufferSize) {
+ AsyncAppender async = new AsyncAppender();
+ async.addAppender(wrappedAppender);
+ async.setBufferSize(bufferSize);
+ async.activateOptions();
+ Logger.getRootLogger().addAppender(async);
+
+ return async;
}
+ /**
+ * Performs post test cleanup.
+ */
public void tearDown() {
+ if (asyncAppender != null) {
+ asyncAppender.close();
+ }
+
LogManager.shutdown();
}
- // this test checks whether it is possible to write to a closed AsyncAppender
- public void test1() throws Exception {
- asyncAppender.setName("async-CloseTest");
+ /**
+ * Tests writing to an AsyncAppender after calling close.
+ */
+ public void testClose() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ asyncAppender =
+ createAsyncAppender(vectorAppender, AsyncAppender.DEFAULT_BUFFER_SIZE);
+ asyncAppender.setName("async-testClose");
+
root.debug("m1");
asyncAppender.close();
root.debug("m2");
+
Vector v = vectorAppender.getVector();
assertEquals(v.size(), 1);
+ assertTrue(vectorAppender.isClosed());
}
- // this test checks whether appenders embedded within an AsyncAppender are
also
- // closed
- public void closeTest() {
- asyncAppender.setName("async-test2");
+ /**
+ * Tests that bad appenders do not silently fail forever
+ * on the dispatching thread.
+ *
+ * @throws InterruptedException if test is interrupted while sleeping.
+ */
+ public void testBadAppender() throws InterruptedException {
+ Appender nullPointerAppender = new NullPointerAppender();
+ asyncAppender =
+ createAsyncAppender(
+ nullPointerAppender, AsyncAppender.DEFAULT_BUFFER_SIZE);
+
+ //
+ // NullPointerException should kill dispatching thread
+ // before sleep returns.
+ root.info("Message");
+ Thread.sleep(100);
+
+ try {
+ //
+ // subsequent call should be synchronous
+ // and result in a NullPointerException on this thread.
+ root.info("Message");
+ fail("Should have thrown exception");
+ } catch (NullPointerException ex) {
+ assertNotNull(ex);
+ }
+ }
+
+ /**
+ * Test logging to AsyncAppender from many threads.
+ * @throws InterruptedException if test is interrupted while sleeping.
+ */
+ public void testManyLoggingThreads() throws InterruptedException {
+ BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+ asyncAppender = createAsyncAppender(blockableAppender, 5);
+
+ //
+ // create threads
+ //
+ final int threadCount = 10;
+ Thread[] threads = new Thread[threadCount];
+ final int repetitions = 100;
+ Greeter greeter = new Greeter(root, repetitions);
+
+ for (int i = 0; i < threads.length; i++) {
+ threads[i] = new Thread(greeter);
+ }
+
+ //
+ // block underlying appender
+ synchronized (blockableAppender.getMonitor()) {
+ //
+ // start threads holding for queue to open up
+ for (int i = 0; i < threads.length; i++) {
+ threads[i].start();
+ }
+ }
+
+ // dispatcher now running free
+ //
+ // wait until all threads complete
+ for (int i = 0; i < threads.length; i++) {
+ threads[i].join(2000);
+ assertFalse(threads[i].isAlive());
+ }
- root.debug("m1");
asyncAppender.close();
- root.debug("m2");
+ assertEquals(
+ threadCount * repetitions, blockableAppender.getVector().size());
+ }
- Vector v = vectorAppender.getVector();
- assertEquals(v.size(), 1);
- assertTrue(vectorAppender.isClosed());
+ /**
+ * Tests interruption handling on logging threads.
+
+ * @throws InterruptedException if test is interrupted while sleeping.
+ */
+ public void testInterruptWhileLogging() throws InterruptedException {
+ BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+ asyncAppender = createAsyncAppender(blockableAppender, 5);
+
+ Thread greeter = new Thread(new Greeter(root, 100));
+
+ synchronized (blockableAppender.getMonitor()) {
+ greeter.start();
+ Thread.sleep(100);
+
+ //
+ // Undesirable behavior: Interrupts are swallowed by
+ // AsycnAppender which could interfere with expected
+ // response to interrupts if the client code called wait or
+ // sleep.
+ //
+ greeter.interrupt();
+ Thread.sleep(10);
+ greeter.interrupt();
+ Thread.sleep(10);
+ greeter.interrupt();
+ }
+
+ greeter.join();
+ asyncAppender.close();
+
+ Vector events = blockableAppender.getVector();
+ assertEquals(100, events.size());
}
- // this test checks whether appenders embedded within an AsyncAppender are
also
- // closed
- public void test2() {
- int LEN = 200;
- asyncAppender.setName("async-test3");
- for (int i = 0; i < LEN; i++) {
- root.debug("message" + i);
+ /**
+ * Tests interruption handling in AsyncAppender.close.
+ *
+ * @throws InterruptedException if test is interrupted while sleeping.
+ *
+ */
+ public void testInterruptWhileClosing() throws InterruptedException {
+ BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+ asyncAppender = createAsyncAppender(blockableAppender, 5);
+
+ Thread greeter = new Thread(new Greeter(root, 100));
+ Thread closer = new Thread(new Closer(asyncAppender));
+
+ synchronized (blockableAppender.getMonitor()) {
+ greeter.start();
+ Thread.sleep(100);
+ closer.start();
+ Thread.sleep(100);
+ closer.interrupt();
}
- System.out.println("Done loop.");
- System.out.flush();
+ greeter.join();
+
+ Vector events = blockableAppender.getVector();
+ assertEquals(7, events.size());
+ }
+
+ /**
+ * Tests killing the dispatch thread.
+ *
+ * @throws InterruptedException if test is interrupted while sleeping.
+ *
+ */
+ public void testInterruptDispatcher() throws InterruptedException {
+ BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+ asyncAppender = createAsyncAppender(blockableAppender, 5);
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+ root.info("Hello, World");
+
+ //
+ // sleep long enough for that to get dispatched
+ //
+ Thread.sleep(50);
+
+ Thread dispatcher = blockableAppender.getDispatcher();
+ assertNotNull(dispatcher);
+ dispatcher.interrupt();
+ Thread.sleep(50);
+
+ //
+ // Undesirable action: interrupting the dispatch thread
+ // removes all appenders.
+ //
+ Enumeration iter = asyncAppender.getAllAppenders();
+ assertTrue((iter == null) || !iter.hasMoreElements());
+ }
+
+ /**
+ * Tests getBufferSize.
+ */
+ public void testGetBufferSize() {
+ asyncAppender = new AsyncAppender();
+ assertEquals(
+ AsyncAppender.DEFAULT_BUFFER_SIZE, asyncAppender.getBufferSize());
+ }
+
+ /**
+ * Tests setBufferSize(0).
+ */
+ public void testSetBufferSizeZero() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ asyncAppender = createAsyncAppender(vectorAppender, 0);
+ assertEquals(0, asyncAppender.getBufferSize());
+
+ //
+ // any logging request will deadlock.
+ //root.debug("m1");
+ //root.debug("m2");
asyncAppender.close();
- root.debug("m2");
Vector v = vectorAppender.getVector();
- assertEquals(v.size(), LEN);
- assertTrue(vectorAppender.isClosed());
+ assertEquals(v.size(), 0);
}
- private static class NullPointerAppender extends AppenderSkeleton {
- public NullPointerAppender() {
- super(true);
- }
+ /**
+ * Tests setBufferSize(-10).
+ */
+ public void testSetBufferSizeNegative() {
+ asyncAppender = new AsyncAppender();
+
+ try {
+ asyncAppender.setBufferSize(-10);
+ fail("Should have thrown NegativeArraySizeException");
+ } catch (NegativeArraySizeException ex) {
+ assertNotNull(ex);
+ }
+ }
+ /**
+ * Tests getAllAppenders.
+ */
+ public void testGetAllAppenders() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ asyncAppender = createAsyncAppender(vectorAppender, 5);
+
+ Enumeration iter = asyncAppender.getAllAppenders();
+ assertTrue(iter.hasMoreElements());
+ assertSame(vectorAppender, iter.nextElement());
+ assertFalse(iter.hasMoreElements());
+ }
- /**
- This method is called by the [EMAIL PROTECTED]
AppenderSkeleton#doAppend}
- method.
-
- */
- public void append(LoggingEvent event) {
- throw new NullPointerException();
- }
+ /**
+ * Tests getAppender.
+ */
+ public void testGetAppender() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ vectorAppender.setName("test");
+ asyncAppender = createAsyncAppender(vectorAppender, 5);
- public void close() {
- }
+ Appender appender = asyncAppender.getAppender("test");
+ assertSame(vectorAppender, appender);
+ }
+
+ /**
+ * Test getLocationInfo.
+ */
+ public void testGetLocationInfo() {
+ asyncAppender = new AsyncAppender();
+ assertFalse(asyncAppender.getLocationInfo());
+ }
+
+ /**
+ * Tests isAttached.
+ */
+ public void testIsAttached() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ asyncAppender = createAsyncAppender(vectorAppender, 5);
+ assertTrue(asyncAppender.isAttached(vectorAppender));
+ assertFalse(asyncAppender.isAttached(asyncAppender));
+ assertFalse(asyncAppender.isAttached(new BlockableVectorAppender()));
+ }
+
+ /**
+ * Tests requiresLayout.
+ *
+ * @deprecated feature under test is deprecated.
+ */
+ public void testRequiresLayout() {
+ asyncAppender = new AsyncAppender();
+ assertFalse(asyncAppender.requiresLayout());
+ }
+
+ /**
+ * Tests removeAllAppenders.
+ */
+ public void testRemoveAllAppenders() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ asyncAppender = new AsyncAppender();
+ asyncAppender.addAppender(vectorAppender);
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+ asyncAppender.removeAllAppenders();
+
+ Enumeration iter = asyncAppender.getAllAppenders();
+ assertTrue((iter == null) || !iter.hasMoreElements());
+ }
+
+ /**
+ * Tests removeAppender(Appender).
+ */
+ public void testRemoveAppender() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ vectorAppender.setName("test");
+ asyncAppender = new AsyncAppender();
+ asyncAppender.addAppender(vectorAppender);
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+
+ VectorAppender appender2 = new VectorAppender();
+ appender2.setName("test");
+ asyncAppender.removeAppender(appender2);
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+ asyncAppender.removeAppender(vectorAppender);
+ assertFalse(asyncAppender.getAllAppenders().hasMoreElements());
+ }
+
+ /**
+ * Tests removeAppender(String).
+ */
+ public void testRemoveAppenderByName() {
+ VectorAppender vectorAppender = createDelayedAppender();
+ vectorAppender.setName("test");
+ asyncAppender = new AsyncAppender();
+ asyncAppender.addAppender(vectorAppender);
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+ asyncAppender.removeAppender("TEST");
+ assertTrue(asyncAppender.getAllAppenders().hasMoreElements());
+ asyncAppender.removeAppender("test");
+ assertFalse(asyncAppender.getAllAppenders().hasMoreElements());
+ }
+
+ /**
+ * Appender that throws a NullPointerException on calls to append.
+ * Used to test behavior of AsyncAppender when dispatching to
+ * misbehaving appenders.
+ */
+ private static final class NullPointerAppender extends AppenderSkeleton {
+ /**
+ * Create new instance.
+ */
+ public NullPointerAppender() {
+ super(true);
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void append(final LoggingEvent event) {
+ throw new NullPointerException();
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void close() {
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public boolean requiresLayout() {
+ return false;
+ }
+ }
- public boolean requiresLayout() {
- return false;
+ /**
+ * Logging request runnable.
+ */
+ private static final class Greeter implements Runnable {
+ /**
+ * Logger.
+ */
+ private final Logger logger;
+
+ /**
+ * Repetitions.
+ */
+ private final int repetitions;
+
+ /**
+ * Create new instance.
+ * @param logger logger, may not be null.
+ * @param repetitions repetitions.
+ */
+ public Greeter(final Logger logger, final int repetitions) {
+ if (logger == null) {
+ throw new IllegalArgumentException("logger");
+ }
+
+ this.logger = logger;
+ this.repetitions = repetitions;
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void run() {
+ synchronized (this) {
+ for (int i = 0; (i < repetitions) && !Thread.interrupted(); i++) {
+ logger.info("Hello, World");
}
+ }
+ }
}
+ /**
+ * Vector appender that can be explicitly blocked.
+ */
+ private static final class BlockableVectorAppender extends VectorAppender {
+ /**
+ * Monitor object used to block appender.
+ */
+ private final Object monitor = new Object();
+
+ /**
+ * Thread of last call to append.
+ */
+ private Thread dispatcher;
+
+ /**
+ * Create new instance.
+ */
+ public BlockableVectorAppender() {
+ super();
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void append(final LoggingEvent event) {
+ synchronized (monitor) {
+ dispatcher = Thread.currentThread();
+ super.append(event);
+ }
+ }
- public void testBadAppender() throws Exception {
- Appender nullPointerAppender = new NullPointerAppender();
- asyncAppender.addAppender(nullPointerAppender);
- asyncAppender.setBufferSize(5);
- asyncAppender.activateOptions();
- root.addAppender(nullPointerAppender);
- try {
- root.info("Message");
- Thread.sleep(10);
- root.info("Message");
- fail("Should have thrown exception");
- } catch(NullPointerException ex) {
+ /**
+ * Get monitor object.
+ * @return monitor.
+ */
+ public Object getMonitor() {
+ return monitor;
+ }
+ /**
+ * Get thread of previous call to append.
+ * @return thread, may be null.
+ */
+ public Thread getDispatcher() {
+ synchronized (monitor) {
+ return dispatcher;
}
+ }
}
+ /**
+ * Closes appender.
+ */
+ private static final class Closer implements Runnable {
+ /**
+ * Appender.
+ */
+ private final AsyncAppender appender;
+
+ /**
+ * Create new instance.
+ * @param appender appender, may not be null.
+ */
+ public Closer(final AsyncAppender appender) {
+ if (appender == null) {
+ throw new IllegalArgumentException("appender");
+ }
+
+ this.appender = appender;
+ }
+
+ /**
+ * [EMAIL PROTECTED]
+ */
+ public void run() {
+ appender.close();
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]