/* * Copyright (c) 2014 the original author or authors * * 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 io.werval.test.util; import java.util.Arrays; import java.util.List; import java.util.Stack; import java.util.concurrent.CopyOnWriteArrayList; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.core.read.ListAppender; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.slf4j.LoggerFactory; import static java.util.stream.Collectors.toList; /** * SLF4J JUnit Rule for use with Logback. */ // Inspired by https://gist.github.com/tux2323/1005996 public class Slf4jRule extends TestWatcher { /** * Logging Level. * * @hidden */ public enum Level { TRACE( ch.qos.logback.classic.Level.TRACE ), DEBUG( ch.qos.logback.classic.Level.DEBUG ), INFO( ch.qos.logback.classic.Level.INFO ), WARN( ch.qos.logback.classic.Level.WARN ), ERROR( ch.qos.logback.classic.Level.ERROR ); private final ch.qos.logback.classic.Level internalLevel; private Level( ch.qos.logback.classic.Level level ) { this.internalLevel = level; } } private final ListAppender<ILoggingEvent> appender = new ListAppender<>(); private final LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); private final List<String> sources = new CopyOnWriteArrayList<>(); private Level level = Level.TRACE; @Override protected void starting( Description description ) { resetContext(); sources.stream().forEach( source -> addAppenderToName( source ) ); appender.start(); } @Override protected void finished( Description description ) { appender.stop(); resetContext(); } public void record( Level level ) { this.level = level; } public void recordForObject( Object obj ) { recordForType( obj.getClass() ); } public void recordForType( Class<?> type ) { recordForName( type.getName() ); } public void recordForPackage( Package pack ) { recordForName( pack.getName() ); } public void recordForName( String name ) { sources.add( name ); addAppenderToName( name ); } public List<String> allStatements() { return appender.list.stream().map( s -> s.getFormattedMessage() ).collect( toList() ); } public boolean contains( String text ) { return appender.list.stream().anyMatch( event -> event.getFormattedMessage().contains( text ) ); } public boolean containsMatching( String regex ) { return appender.list.stream().anyMatch( event -> event.getFormattedMessage().matches( regex ) ); } public boolean containsExMessage( String exceptionMessage ) { return appender.list.stream().anyMatch( event -> { IThrowableProxy ex = event.getThrowableProxy(); if( ex == null ) { return false; } if( ex.getMessage().contains( exceptionMessage ) ) { return true; } Stack<IThrowableProxy> stack = new Stack<>(); stack.add( ex.getCause() ); stack.addAll( Arrays.asList( ex.getSuppressed() ) ); while( !stack.empty() ) { IThrowableProxy candidate = stack.pop(); if( candidate.getMessage().contains( exceptionMessage ) ) { return true; } stack.add( candidate.getCause() ); stack.addAll( Arrays.asList( candidate.getSuppressed() ) ); } return false; } ); } public int size() { return appender.list.size(); } public void clear() { appender.list.clear(); } private <T> void addAppenderToName( String name ) { Logger logger = (Logger) LoggerFactory.getLogger( name ); logger.addAppender( appender ); logger.setLevel( level.internalLevel ); } private void resetContext() { context.reset(); } }