/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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.
*/
package com.liferay.portal.test.rule.callback;
import com.liferay.portal.kernel.dao.db.DB;
import com.liferay.portal.kernel.dao.db.DBManagerUtil;
import com.liferay.portal.kernel.io.unsync.UnsyncPrintWriter;
import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
import com.liferay.portal.kernel.test.rule.callback.TestCallback;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.test.log.CaptureAppender;
import com.liferay.portal.test.log.Log4JLoggerTestUtil;
import com.liferay.portal.test.rule.ExpectedDBType;
import com.liferay.portal.test.rule.ExpectedLog;
import com.liferay.portal.test.rule.ExpectedLogs;
import com.liferay.portal.test.rule.ExpectedMultipleLogs;
import com.liferay.portal.test.rule.ExpectedType;
import com.liferay.portal.test.rule.LogAssertionAppender;
import com.liferay.portal.test.rule.LogAssertionHandler;
import com.liferay.portal.test.rule.LogAssertionUncaughtExceptionHandler;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.Assert;
import org.junit.runner.Description;
/**
* @author Shuyang Zhou
*/
public class LogAssertionTestCallback
implements TestCallback<List<CaptureAppender>, List<CaptureAppender>> {
public static final LogAssertionTestCallback INSTANCE =
new LogAssertionTestCallback();
public static void caughtFailure(Error error) {
Thread currentThread = Thread.currentThread();
if (currentThread == _thread) {
throw error;
}
Error previousError = _concurrentFailures.put(currentThread, error);
if (previousError != null) {
error.addSuppressed(previousError);
}
}
public static void endAssert(
List<ExpectedLogs> expectedLogsList,
List<CaptureAppender> captureAppenders) {
StringBundler sb = new StringBundler();
for (CaptureAppender captureAppender : captureAppenders) {
try {
for (LoggingEvent loggingEvent :
captureAppender.getLoggingEvents()) {
String renderedMessage = loggingEvent.getRenderedMessage();
if (!isExpected(expectedLogsList, renderedMessage)) {
sb.append(renderedMessage);
sb.append("\n\n");
}
}
}
finally {
captureAppender.close();
}
}
if (sb.index() != 0) {
sb.setIndex(sb.index() - 1);
Assert.fail(sb.toString());
}
Thread.setDefaultUncaughtExceptionHandler(_uncaughtExceptionHandler);
_thread = null;
try {
for (Map.Entry<Thread, Error> entry :
_concurrentFailures.entrySet()) {
Thread thread = entry.getKey();
Error error = entry.getValue();
UnsyncStringWriter unsyncStringWriter =
new UnsyncStringWriter();
error.printStackTrace(
new UnsyncPrintWriter(unsyncStringWriter));
sb.append("Thread ");
sb.append(thread);
sb.append(" caught concurrent failure: ");
sb.append(error);
sb.append("\n");
sb.append(unsyncStringWriter.toString());
sb.append("\n\n");
}
if (sb.index() != 0) {
sb.setIndex(sb.index() - 1);
Assert.fail(sb.toString());
}
}
finally {
_concurrentFailures.clear();
}
}
public static List<CaptureAppender> startAssert(
List<ExpectedLogs> expectedLogsList) {
_thread = Thread.currentThread();
_uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(
new LogAssertionUncaughtExceptionHandler(
_uncaughtExceptionHandler));
List<CaptureAppender> captureAppenders = new ArrayList<>(
expectedLogsList.size());
for (ExpectedLogs expectedLogs : expectedLogsList) {
Class<?> clazz = expectedLogs.loggerClass();
captureAppenders.add(
Log4JLoggerTestUtil.configureLog4JLogger(
clazz.getName(), Level.toLevel(expectedLogs.level())));
}
installJdk14Handler();
installLog4jAppender();
return captureAppenders;
}
@Override
public void afterClass(
Description description, List<CaptureAppender> captureAppenders) {
ExpectedMultipleLogs expectedMultipleLogs = description.getAnnotation(
ExpectedMultipleLogs.class);
List<ExpectedLogs> expectedLogsList = new ArrayList<>();
if (expectedMultipleLogs == null) {
ExpectedLogs expectedLogs = description.getAnnotation(
ExpectedLogs.class);
if (expectedLogs != null) {
expectedLogsList.add(expectedLogs);
}
}
else {
Collections.addAll(
expectedLogsList, expectedMultipleLogs.expectedMultipleLogs());
}
endAssert(expectedLogsList, captureAppenders);
}
@Override
public void afterMethod(
Description description, List<CaptureAppender> captureAppenders,
Object target) {
afterClass(description, captureAppenders);
}
@Override
public List<CaptureAppender> beforeClass(Description description) {
ExpectedMultipleLogs expectedMultipleLogs = description.getAnnotation(
ExpectedMultipleLogs.class);
List<ExpectedLogs> expectedLogsList = new ArrayList<>();
if (expectedMultipleLogs == null) {
ExpectedLogs expectedLogs = description.getAnnotation(
ExpectedLogs.class);
if (expectedLogs != null) {
expectedLogsList.add(expectedLogs);
}
}
else {
Collections.addAll(
expectedLogsList, expectedMultipleLogs.expectedMultipleLogs());
}
return startAssert(expectedLogsList);
}
@Override
public List<CaptureAppender> beforeMethod(
Description description, Object target) {
return beforeClass(description);
}
protected static void installJdk14Handler() {
Logger logger = Logger.getLogger(StringPool.BLANK);
logger.removeHandler(LogAssertionHandler.INSTANCE);
logger.addHandler(LogAssertionHandler.INSTANCE);
}
protected static void installLog4jAppender() {
org.apache.log4j.Logger logger =
org.apache.log4j.Logger.getRootLogger();
logger.removeAppender(LogAssertionAppender.INSTANCE);
logger.addAppender(LogAssertionAppender.INSTANCE);
}
protected static boolean isExpected(
List<ExpectedLogs> expectedLogsList, String renderedMessage) {
for (ExpectedLogs expectedLogs : expectedLogsList) {
for (ExpectedLog expectedLog : expectedLogs.expectedLogs()) {
ExpectedDBType expectedDBType = expectedLog.expectedDBType();
if (expectedDBType != ExpectedDBType.NONE) {
DB db = DBManagerUtil.getDB();
if (expectedDBType.getDBType() != db.getDBType()) {
continue;
}
}
ExpectedType expectedType = expectedLog.expectedType();
if (expectedType == ExpectedType.CONTAINS) {
if (renderedMessage.contains(expectedLog.expectedLog())) {
return true;
}
}
else if (expectedType == ExpectedType.EXACT) {
if (renderedMessage.equals(expectedLog.expectedLog())) {
return true;
}
}
else if (expectedType == ExpectedType.POSTFIX) {
if (renderedMessage.endsWith(expectedLog.expectedLog())) {
return true;
}
}
else if (expectedType == ExpectedType.PREFIX) {
if (renderedMessage.startsWith(expectedLog.expectedLog())) {
return true;
}
}
}
}
return false;
}
private LogAssertionTestCallback() {
}
private static final Map<Thread, Error> _concurrentFailures =
new ConcurrentHashMap<>();
private static volatile Thread _thread;
private static volatile UncaughtExceptionHandler _uncaughtExceptionHandler;
}