1
0

Fix up log4j12 tests so no log4j lib needs to be in the classpath

when the suite() method is called on any TestCase class.


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/logging/trunk@209737 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Simon Kitching
2005-07-08 11:45:25 +00:00
parent a9958f04f8
commit 697b414639
7 changed files with 183 additions and 124 deletions

View File

@@ -22,17 +22,15 @@ import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.List;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator; import java.util.Iterator;
import java.util.Properties; import java.util.Properties;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.Log4J12Logger; import org.apache.commons.logging.impl.Log4J12Logger;
@@ -47,24 +45,40 @@ import org.apache.commons.logging.impl.Log4J12Logger;
public abstract class StandardTests extends TestCase { public abstract class StandardTests extends TestCase {
// ------------------------------------------------------------------- /**
// Constants * Simple structure to store information about messages that actually get
// ------------------------------------------------------------------- * logged by the underlying logging library.
*/
public static class LogEvent {
public String msg;
public String level;
public Throwable throwable;
}
/** /**
* The set of message strings that methods logPlainMessages and * Simple helper class that can configure log4j to redirect all logged
* logExceptionMessages output when called. * messages into a list of LogEvent messages.
* <p>
* The TestCase classes that junit will run later have two roles: they
* hold the tests to run, and they also provide the suite() method that
* indicates which tests to run. This causes complications for us in the
* case of log4j because of the binary-incompatible log4j versions. We
* can't have any version of log4j to be in the classpath until we are
* actually running the tests returned by suite() - but junit can't load
* the class to call suite() on it if the class or any of its ancestors
* have direct references to log4j APIs (or NoClassDefFound occurs).
* <p>
* The answer is to move all the direct log4j calls out of the TestCase
* classes into a helper which is only loaded via reflection during the
* test runs (and not during calls to suite()). This class defines the
* interface required of that helper.
* <p>
* See also method getTestHelperClassName.
*/ */
private static final String TEST_MESSAGES[] = {
"info", "warn", "error", "fatal"
};
/** public static interface TestHelper {
* The message levels that the messages in TEST_MESSAGES are logged at. public void forwardMessages(List logEvents);
*/ }
private static final Level TEST_LEVELS[] = {
Level.INFO, Level.WARN, Level.ERROR, Level.FATAL
};
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// JUnit Infrastructure Methods // JUnit Infrastructure Methods
@@ -84,17 +98,13 @@ public abstract class StandardTests extends TestCase {
LogFactory.releaseAll(); LogFactory.releaseAll();
} }
// ----------------------------------------------------------- Test Methods // -----------------------------------------------------------
// abstract methods
// -----------------------------------------------------------
/** protected abstract String getTestHelperClassName();
* Test that our test harness code works, ie that we are able to
* configure log4j on the fly to write to an instance of TestAppender. // ----------------------------------------------------------- Test Methods
*/
public void testAppender() throws Exception {
setUpTestAppender();
TestAppender testAppender = getTestAppender();
assertNotNull("Appender exists", testAppender);
}
/** /**
* Test that a LogFactory gets created as expected. * Test that a LogFactory gets created as expected.
@@ -111,46 +121,34 @@ public abstract class StandardTests extends TestCase {
assertEquals("Names empty", 0, names.length); assertEquals("Names empty", 0, names.length);
} }
/**
* Test that a Log object gets created as expected.
*/
public void testCreateLog() throws Exception {
setUpTestAppender();
Log log = LogFactory.getLog("test-category");
// check that it is of the expected type, that we can access
// the underlying real logger and that the logger level has
// been set as expected after the call to setUpTestAppender.
Log4J12Logger log4j12 = (Log4J12Logger) log;
Logger logger = log4j12.getLogger();
assertEquals("Logger name", "test-category", logger.getName());
assertEquals("Logger level", Level.INFO, logger.getEffectiveLevel());
}
/** /**
* Verify that we can log messages without exceptions. * Verify that we can log messages without exceptions.
*/ */
public void testPlainMessages() throws Exception { public void testPlainMessages() throws Exception {
setUpTestAppender(); List logEvents = new ArrayList();
setUpTestAppender(logEvents);
Log log = LogFactory.getLog("test-category"); Log log = LogFactory.getLog("test-category");
logPlainMessages(log); logPlainMessages(log);
checkLoggingEvents(false); checkLoggingEvents(logEvents, false);
} }
/** /**
* Verify that we can log exception messages. * Verify that we can log exception messages.
*/ */
public void testExceptionMessages() throws Exception { public void testExceptionMessages() throws Exception {
setUpTestAppender(); List logEvents = new ArrayList();
setUpTestAppender(logEvents);
Log log = LogFactory.getLog("test-category"); Log log = LogFactory.getLog("test-category");
logExceptionMessages(log); logExceptionMessages(log);
checkLoggingEvents(true); checkLoggingEvents(logEvents, true);
} }
/** /**
* Test Serializability of Log instance * Test Serializability of Log instance
*/ */
public void testSerializable() throws Exception { public void testSerializable() throws Exception {
List logEvents = new ArrayList();
setUpTestAppender(logEvents);
Log log = LogFactory.getLog("test-category"); Log log = LogFactory.getLog("test-category");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -165,37 +163,23 @@ public abstract class StandardTests extends TestCase {
// Check the characteristics of the resulting object // Check the characteristics of the resulting object
logExceptionMessages(newLog); logExceptionMessages(newLog);
checkLoggingEvents(true); checkLoggingEvents(logEvents, true);
} }
// -------------------------------------------------------- Support Methods // -------------------------------------------------------- Support Methods
/** /**
* Call log4j's PropertyConfigurator passing specific config info * Modify log4j's setup so that all messages actually logged get redirected
* in order to force log4j to create an instance of class TestAppender * into the specified list.
* and send all logged messages to that appender object.
* <p>
* The TestAppender class stores all its messages in memory, so we
* can later check what messages it received from log4j.
* <p> * <p>
* This method also sets the logging level to INFO so that we * This method also sets the logging level to INFO so that we
* can test whether messages are getting properly filtered. * can test whether messages are getting properly filtered.
*/ */
private void setUpTestAppender() throws Exception { private void setUpTestAppender(List logEvents) throws Exception {
Properties props = new Properties(); String testHelperClassName = getTestHelperClassName();
props.put("log4j.rootLogger", "INFO, A1"); Class clazz = this.getClass().getClassLoader().loadClass(testHelperClassName);
props.put("log4j.appender.A1", "org.apache.commons.logging.log4j.TestAppender"); TestHelper testHelper = (TestHelper) clazz.newInstance();
PropertyConfigurator.configure(props); testHelper.forwardMessages(logEvents);
}
/**
* Get the custom TestAppender that has been set to recieve all
* messages logged via log4j. It is presumed that method setUpTestAppender
* has been called earlier to force a TestAppender to be used by log4j.
*/
private TestAppender getTestAppender() {
Enumeration appenders = Logger.getRootLogger().getAllAppenders();
return (TestAppender) appenders.nextElement();
} }
/** /**
@@ -207,36 +191,37 @@ public abstract class StandardTests extends TestCase {
* called to log a known number of messages at known levels. * called to log a known number of messages at known levels.
* </ul> * </ul>
* *
* @param logEvents is the list of log events received.
*
* @param thrown False if logPlainMessages was called * @param thrown False if logPlainMessages was called
* (ie the TestAppender is expected to have received * (ie the TestAppender is expected to have received
* logevents with no associated exception info). True if * logevents with no associated exception info). True if
* logExceptionMessages was called. * logExceptionMessages was called.
*/ */
private void checkLoggingEvents(boolean thrown) { private void checkLoggingEvents(List logEvents, boolean thrown) {
TestAppender appender = getTestAppender(); LogEvent ev;
Iterator events = appender.events();
for (int i = 0; i < TEST_MESSAGES.length; i++) {
assertTrue("Logged event " + i + " exists",events.hasNext());
LoggingEvent event = (LoggingEvent) events.next();
assertEquals("LoggingEvent level",
TEST_LEVELS[i], event.getLevel());
assertEquals("LoggingEvent message",
TEST_MESSAGES[i], event.getMessage());
if (thrown) { assertEquals("Unexpected number of log events", 4, logEvents.size());
assertNotNull("LoggingEvent thrown",
event.getThrowableInformation().getThrowableStrRep()); ev = (LogEvent) logEvents.get(0);
assertTrue("LoggingEvent thrown type", assertEquals("Info message expected", "info", ev.msg);
event.getThrowableInformation() assertEquals("Info level expected", "INFO", ev.level);
.getThrowableStrRep()[0] assertEquals("Exception data incorrect", (ev.throwable!=null), thrown);
.indexOf("IndexOutOfBoundsException")>0);
} else { ev = (LogEvent) logEvents.get(1);
assertNull("LoggingEvent thrown", assertEquals("Warn message expected", "warn", ev.msg);
event.getThrowableInformation()); assertEquals("Warn level expected", "WARN", ev.level);
} assertEquals("Exception data incorrect", (ev.throwable!=null), thrown);
}
assertTrue(!events.hasNext()); ev = (LogEvent) logEvents.get(2);
appender.flush(); assertEquals("Error message expected", "error", ev.msg);
assertEquals("Error level expected", "ERROR", ev.level);
assertEquals("Exception data incorrect", (ev.throwable!=null), thrown);
ev = (LogEvent) logEvents.get(3);
assertEquals("Fatal message expected", "fatal", ev.msg);
assertEquals("Fatal level expected", "FATAL", ev.level);
assertEquals("Exception data incorrect", (ev.throwable!=null), thrown);
} }
@@ -252,7 +237,7 @@ public abstract class StandardTests extends TestCase {
log.fatal("fatal"); log.fatal("fatal");
} }
/* /**
* Log messages with exceptions * Log messages with exceptions
*/ */
private void logExceptionMessages(Log log) { private void logExceptionMessages(Log log) {

View File

@@ -49,4 +49,12 @@ public class ApiClasspathStandardTestCase extends StandardTests {
Class testClass = child.loadClass(thisClass.getName()); Class testClass = child.loadClass(thisClass.getName());
return new PathableTestSuite(testClass, child); return new PathableTestSuite(testClass, child);
} }
/**
* Return the name of a class that makes all direct calls to log4j
* apis. See StandardTests.TestHelper for more information.
*/
public String getTestHelperClassName() {
return "org.apache.commons.logging.log4j.log4j12.TestHelper";
}
} }

View File

@@ -44,4 +44,12 @@ public class AppClasspathStandardTestCase extends StandardTests {
Class testClass = loader.loadClass(thisClass.getName()); Class testClass = loader.loadClass(thisClass.getName());
return new PathableTestSuite(testClass, loader); return new PathableTestSuite(testClass, loader);
} }
/**
* Return the name of a class that makes all direct calls to log4j
* apis. See StandardTests.TestHelper for more information.
*/
public String getTestHelperClassName() {
return "org.apache.commons.logging.log4j.log4j12.TestHelper";
}
} }

View File

@@ -47,4 +47,12 @@ public class ChildClasspathStandardTestCase extends StandardTests {
Class testClass = child.loadClass(thisClass.getName()); Class testClass = child.loadClass(thisClass.getName());
return new PathableTestSuite(testClass, child); return new PathableTestSuite(testClass, child);
} }
/**
* Return the name of a class that makes all direct calls to log4j
* apis. See StandardTests.TestHelper for more information.
*/
public String getTestHelperClassName() {
return "org.apache.commons.logging.log4j.log4j12.TestHelper";
}
} }

View File

@@ -46,4 +46,12 @@ public class ParentClasspathStandardTestCase extends StandardTests {
Class testClass = child.loadClass(thisClass.getName()); Class testClass = child.loadClass(thisClass.getName());
return new PathableTestSuite(testClass, child); return new PathableTestSuite(testClass, child);
} }
/**
* Return the name of a class that makes all direct calls to log4j
* apis. See StandardTests.TestHelper for more information.
*/
public String getTestHelperClassName() {
return "org.apache.commons.logging.log4j.log4j12.TestHelper";
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2001-2004 The Apache Software Foundation. * Copyright 2005 The Apache Software Foundation.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,55 +14,59 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.commons.logging.log4j; package org.apache.commons.logging.log4j.log4j12;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.LoggingEvent;
import org.apache.commons.logging.log4j.StandardTests;
/** /**
* A custom implementation of <code>org.apache.log4j.Appender</code> which * A custom implementation of <code>org.apache.log4j.Appender</code> which
* stores all received log event messages in memory. This allows test code * converts the log4j-specific log event record into a representation that
* to check whether the messages it expected to log have actually been logged. * doesn't have a dependency on log4j and stores that new representation into
* * an external list.
*
* @author Craig R. McClanahan
* @version $Revision$ $Date$
*/ */
public class TestAppender extends AppenderSkeleton { public class TestAppender extends AppenderSkeleton {
/**
* Constructor.
*/
public TestAppender(List logEvents) {
events = logEvents;
}
// ----------------------------------------------------- Instance Variables // ----------------------------------------------------- Instance Variables
// The set of logged events for this appender // The set of logged events for this appender
private List events = new ArrayList(); private List events;
// --------------------------------------------------------- Public Methods
public Iterator events() {
return (events.iterator());
}
public void flush() {
events.clear();
}
// ------------------------------------------------------- Appender Methods // ------------------------------------------------------- Appender Methods
protected void append(LoggingEvent event) { protected void append(LoggingEvent event) {
events.add(event); StandardTests.LogEvent lev = new StandardTests.LogEvent();
lev.level = event.getLevel().toString();
if (event.getMessage() == null)
lev.msg = null;
else
lev.msg = event.getMessage().toString();
if (event.getThrowableInformation() == null)
lev.throwable = null;
else
lev.throwable = event.getThrowableInformation().getThrowable();
events.add(lev);
} }

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.logging.log4j.log4j12;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.commons.logging.log4j.StandardTests;
/**
* See StandardTests.TestHelper for information on this class.
*/
public class TestHelper implements StandardTests.TestHelper {
public void forwardMessages(List logEvents) {
TestAppender appender = new TestAppender(logEvents);
Logger rootLogger = Logger.getRootLogger();
rootLogger.removeAllAppenders();
rootLogger.addAppender(appender);
rootLogger.setLevel(Level.INFO);
}
}