logging-log4j-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From carn...@apache.org
Subject svn commit: r386111 - /logging/log4j/trunk/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
Date Wed, 15 Mar 2006 16:46:01 GMT
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 {@link 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);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void append(final LoggingEvent event) {
+      throw new NullPointerException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close() {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    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;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    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();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    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;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void run() {
+      appender.close();
+    }
+  }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org


Mime
View raw message