HHH-9658 Simplify the way tests assert on logging statements

This commit is contained in:
Sanne Grinovero 2015-02-27 10:56:23 +00:00
parent 965e044850
commit c067fc2b42
12 changed files with 645 additions and 0 deletions

View File

@ -5,11 +5,17 @@ dependencies {
compile( libraries.byteman_install )
compile( libraries.byteman_bmunit )
compile( libraries.xapool )
compile( libraries.log4j )
compile ( libraries.jboss_jta ) {
transitive=false;
}
}
// resources inherently exclude sources
sourceSets.test.resources {
setSrcDirs( ['src/test/java'] )
}
// todo : Fold into hibernate-core and publish in separate publications
// once http://issues.gradle.org/browse/GRADLE-2966 is resolved;
// that will allow us to keep the same artifactId and publish the pom

View File

@ -0,0 +1,119 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import org.jboss.logging.Logger;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;
/**
* A {@code Logger} implementation which delegates to Log4J but makes it possible
* to test for events being logged (not logged).
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2015 Red Hat Inc.
*/
public final class Log4DelegatingLogger extends Logger {
private final org.apache.log4j.Logger logger;
// Synchronize access on the field
private final List<LogListener> enabledListeners = new LinkedList<LogListener>();
Log4DelegatingLogger(final String name) {
super( name );
logger = org.apache.log4j.Logger.getLogger( name );
}
void registerListener(LogListener newListener) {
synchronized ( enabledListeners ) {
if ( newListener != null ) {
enabledListeners.add( newListener );
}
}
}
void clearAllListeners() {
synchronized ( enabledListeners ) {
enabledListeners.clear();
}
}
public boolean isEnabled(final Level level) {
final org.apache.log4j.Level l = translate( level );
return logger.isEnabledFor( l ) && l.isGreaterOrEqual( logger.getEffectiveLevel() );
}
protected void doLog(final Level level, final String loggerClassName, final Object message, final Object[] parameters, final Throwable thrown) {
final org.apache.log4j.Level translatedLevel = translate( level );
intercept( level, parameters == null || parameters.length == 0 ? String.valueOf( message ) : MessageFormat.format( String.valueOf( message ), parameters ), thrown );
if ( logger.isEnabledFor( translatedLevel ) )
try {
logger.log( loggerClassName, translatedLevel,
parameters == null || parameters.length == 0 ? String.valueOf( message ) : MessageFormat.format( String.valueOf( message ), parameters ), thrown );
}
catch (Throwable ignored) {
}
}
private void intercept(Level level, String renderedMessage, Throwable thrown) {
synchronized ( enabledListeners ) {
for ( LogListener listener : enabledListeners ) {
listener.loggedEvent( level, renderedMessage, thrown );
}
}
}
protected void doLogf(final Level level, final String loggerClassName, final String format, final Object[] parameters, final Throwable thrown) {
final org.apache.log4j.Level translatedLevel = translate( level );
intercept( level, parameters == null ? String.format( format ) : String.format( format, parameters ), thrown );
if ( logger.isEnabledFor( translatedLevel ) )
try {
logger.log( loggerClassName, translatedLevel, parameters == null ? String.format( format ) : String.format( format, parameters ), thrown );
}
catch (Throwable ignored) {
}
}
private static org.apache.log4j.Level translate(final Level level) {
if ( level != null )
switch ( level ) {
case FATAL:
return org.apache.log4j.Level.FATAL;
case ERROR:
return org.apache.log4j.Level.ERROR;
case WARN:
return org.apache.log4j.Level.WARN;
case INFO:
return org.apache.log4j.Level.INFO;
case DEBUG:
return org.apache.log4j.Level.DEBUG;
case TRACE:
return org.apache.log4j.Level.TRACE;
}
return org.apache.log4j.Level.ALL;
}
}

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import java.lang.reflect.Field;
import org.hibernate.AssertionFailure;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.DelegatingBasicLogger;
/**
* Test helper to listen for logging events.
* For this to work, it requires JBoss Logging to pick up our custom
* implementation {@code Log4DelegatingLogger} via ServiceLoader.
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2015 Red Hat Inc.
*/
final class LogInspectionHelper {
private LogInspectionHelper() {
}
static void registerListener(LogListener listener, BasicLogger log) {
convertType( log ).registerListener( listener );
}
static void clearAllListeners(BasicLogger log) {
convertType( log ).clearAllListeners();
}
private static Log4DelegatingLogger convertType(BasicLogger log) {
if ( log instanceof DelegatingBasicLogger) {
//Most loggers generated via the annotation processor are of this type
DelegatingBasicLogger wrapper = (DelegatingBasicLogger) log;
try {
return extractFromWrapper( wrapper );
}
catch (Exception cause) {
throw new RuntimeException( cause );
}
}
if ( ! ( log instanceof Log4DelegatingLogger ) ) {
throw new AssertionFailure( "Unexpected log type: JBoss Logger didn't register the custom TestableLoggerProvider as logger provider" );
}
return (Log4DelegatingLogger) log;
}
private static Log4DelegatingLogger extractFromWrapper(DelegatingBasicLogger wrapper) throws Exception {
Field field = DelegatingBasicLogger.class.getDeclaredField( "log" );
field.setAccessible( true );
Object object = field.get( wrapper );
return convertType( (BasicLogger) object );
}
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import org.jboss.logging.Logger.Level;
interface LogListener {
void loggedEvent(Level level, String renderedMessage, Throwable thrown);
}

View File

@ -0,0 +1,57 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import org.jboss.logging.BasicLogger;
import org.junit.rules.ExternalResource;
public final class LoggerInspectionRule extends ExternalResource {
private final BasicLogger log;
public LoggerInspectionRule(BasicLogger log) {
this.log = log;
}
@Override
protected void before() throws Throwable {
// do nothing
}
@Override
protected void after() {
LogInspectionHelper.clearAllListeners( log );
}
public void registerListener(LogListener listener) {
LogInspectionHelper.registerListener( listener, log );
}
public Triggerable watchForLogMessages(String prefix) {
TriggerOnPrefixLogListener listener = new TriggerOnPrefixLogListener( prefix );
registerListener( listener );
return listener;
}
}

View File

@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.log4j.MDC;
import org.apache.log4j.NDC;
import org.jboss.logging.Logger;
/**
* A {@code LoggerProvider} for JBoss Logger.
* See also META-INF/services/org.jboss.logging.LoggerProvider
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2015 Red Hat Inc.
*/
public class TestableLoggerProvider implements org.jboss.logging.LoggerProvider {
//We LEAK Logger instances: good only for testing as we know the set of categories is limited in practice
private static final ConcurrentMap<String,Logger> reuseLoggerInstances = new ConcurrentHashMap<String,Logger>();
// Maintainer note:
// Except the next method, which is adjusted to return our own Log4DelegatingLogger
// this class is a verbatim copy of org.jboss.logging.Log4jLoggerProvider
// (which is a final class)
public Logger getLogger(final String name) {
Logger logger = reuseLoggerInstances.get( name );
if ( logger == null ) {
logger = new Log4DelegatingLogger( "".equals( name ) ? "ROOT" : name );
Logger previous = reuseLoggerInstances.putIfAbsent( name, logger );
if ( previous != null ) {
return previous;
}
}
return logger;
}
public Object getMdc(String key) {
return MDC.get( key );
}
@SuppressWarnings("unchecked")
public Map<String, Object> getMdcMap() {
return MDC.getContext();
}
public Object putMdc(String key, Object val) {
try {
return MDC.get( key );
}
finally {
MDC.put( key, val );
}
}
public void removeMdc(String key) {
MDC.remove( key );
}
public void clearNdc() {
NDC.remove();
}
public String getNdc() {
return NDC.get();
}
public int getNdcDepth() {
return NDC.getDepth();
}
public String peekNdc() {
return NDC.peek();
}
public String popNdc() {
return NDC.pop();
}
public void pushNdc(String message) {
NDC.push( message );
}
public void setNdcMaxDepth(int maxDepth) {
NDC.setMaxDepth( maxDepth );
}
}

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.logging.Logger.Level;
final class TriggerOnPrefixLogListener implements LogListener, Triggerable {
private final String expectedPrefix;
private final AtomicBoolean triggered = new AtomicBoolean( false );
public TriggerOnPrefixLogListener(String expectedPrefix) {
this.expectedPrefix = expectedPrefix;
}
@Override
public void loggedEvent(Level level, String renderedMessage, Throwable thrown) {
if ( renderedMessage != null && renderedMessage.startsWith( expectedPrefix ) ) {
triggered.set( true );
}
}
@Override
public boolean wasTriggered() {
return triggered.get();
}
@Override
public void reset() {
triggered.set( false );
}
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
public interface Triggerable {
boolean wasTriggered();
void reset();
}

View File

@ -0,0 +1 @@
org.hibernate.testing.logger.TestableLoggerProvider

View File

@ -0,0 +1,93 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import static org.junit.Assert.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.nullValue;
import org.hibernate.testing.TestForIssue;
import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
import org.junit.Test;
/**
* Tests the TestHelper ..
* Verifies the Logger interception capabilities which we might use in other tests
* are working as expected.
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2015 Red Hat Inc.
*/
@TestForIssue(jiraKey = "HHH-9658")
public class LogDelegationTest {
private static final Logger LOG = Logger.getLogger( LogDelegationTest.class.getName() );
@Test
public void testLogDelegationIsActivated() {
assertThat( LOG, instanceOf( Log4DelegatingLogger.class ) );
}
@Test
public void testRecording() {
TestListener listener = new TestListener();
LogInspectionHelper.registerListener( listener, LOG );
LOG.debug( "Hey coffee is ready!" );
assertThat( listener.isCAlled, is( true ) );
assertThat( listener.level, is( Level.DEBUG ) );
assertThat( (String) listener.renderedMessage, is( "Hey coffee is ready!" ) );
assertThat( listener.thrown, nullValue() );
LogInspectionHelper.clearAllListeners( LOG );
}
@Test
public void testClearListeners() {
TestListener listener = new TestListener();
LogInspectionHelper.registerListener( listener, LOG );
LogInspectionHelper.clearAllListeners( LOG );
LOG.debug( "Hey coffee is ready!" );
assertThat( listener.isCAlled, is( false ) );
}
private static class TestListener implements LogListener {
boolean isCAlled = false;
Level level;
String renderedMessage;
Throwable thrown;
@Override
public void loggedEvent(Level level, String renderedMessage, Throwable thrown) {
this.level = level;
this.renderedMessage = renderedMessage;
this.thrown = thrown;
this.isCAlled = true;
}
}
}

View File

@ -0,0 +1,61 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2015, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.logger;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionImpl;
import org.hibernate.testing.TestForIssue;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
/**
* Example usage for the JUnit rule to assert logging events
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2015 Red Hat Inc.
*/
@TestForIssue(jiraKey = "HHH-9658")
public class LoggingRuleTest {
//Taking this specific logger as a representative example of a Logger
//(The purpose of this test is not to log but to exercise the logger methods)
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, SessionImpl.class.getName() );
//We'll generally not be able to access the same LOG *instance* so make sure a fresh lookup
//from Logger#getMessageLogger will work fine as well
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule( Logger.getMessageLogger( CoreMessageLogger.class, SessionImpl.class.getName() ) );
@Test
public void testRule() {
Triggerable triggerable = logInspection.watchForLogMessages( "HHH000008:" );
Assert.assertFalse( triggerable.wasTriggered() );
LOG.autoFlushWillNotWork(); //Uses code HHH000008
Assert.assertTrue( triggerable.wasTriggered() );
triggerable.reset();
Assert.assertFalse( triggerable.wasTriggered() );
}
}