001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.test.rule.callback;
016    
017    import com.liferay.portal.kernel.dao.db.DB;
018    import com.liferay.portal.kernel.dao.db.DBManagerUtil;
019    import com.liferay.portal.kernel.test.rule.callback.TestCallback;
020    import com.liferay.portal.kernel.util.StringPool;
021    import com.liferay.portal.test.log.CaptureAppender;
022    import com.liferay.portal.test.log.Log4JLoggerTestUtil;
023    import com.liferay.portal.test.rule.ExpectedDBType;
024    import com.liferay.portal.test.rule.ExpectedLog;
025    import com.liferay.portal.test.rule.ExpectedLogs;
026    import com.liferay.portal.test.rule.ExpectedMultipleLogs;
027    import com.liferay.portal.test.rule.ExpectedType;
028    import com.liferay.portal.test.rule.LogAssertionAppender;
029    import com.liferay.portal.test.rule.LogAssertionHandler;
030    import com.liferay.portal.test.rule.LogAssertionUncaughtExceptionHandler;
031    
032    import java.lang.Thread.UncaughtExceptionHandler;
033    
034    import java.util.ArrayList;
035    import java.util.Arrays;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.concurrent.ConcurrentHashMap;
039    import java.util.logging.Logger;
040    
041    import org.apache.log4j.Level;
042    import org.apache.log4j.spi.LoggingEvent;
043    
044    import org.junit.Assert;
045    import org.junit.runner.Description;
046    
047    /**
048     * @author Shuyang Zhou
049     */
050    public class LogAssertionTestCallback
051            implements TestCallback<List<CaptureAppender>, List<CaptureAppender>> {
052    
053            public static final LogAssertionTestCallback INSTANCE =
054                    new LogAssertionTestCallback();
055    
056            public static void caughtFailure(Error error) {
057                    Thread currentThread = Thread.currentThread();
058    
059                    if (currentThread != _thread) {
060                            _concurrentFailures.put(currentThread, error);
061    
062                            _thread.interrupt();
063                    }
064                    else {
065                            throw error;
066                    }
067            }
068    
069            public static void endAssert(
070                    List<ExpectedLogs> expectedLogsList,
071                    List<CaptureAppender> captureAppenders) {
072    
073                    for (CaptureAppender captureAppender : captureAppenders) {
074                            try {
075                                    for (LoggingEvent loggingEvent :
076                                                    captureAppender.getLoggingEvents()) {
077    
078                                            String renderedMessage = loggingEvent.getRenderedMessage();
079    
080                                            if (!isExpected(expectedLogsList, renderedMessage)) {
081                                                    Assert.fail(renderedMessage);
082                                            }
083                                    }
084                            }
085                            finally {
086                                    captureAppender.close();
087                            }
088                    }
089    
090                    Thread.setDefaultUncaughtExceptionHandler(_uncaughtExceptionHandler);
091    
092                    _thread = null;
093    
094                    try {
095                            for (Map.Entry<Thread, Error> entry :
096                                            _concurrentFailures.entrySet()) {
097    
098                                    Thread thread = entry.getKey();
099                                    Error error = entry.getValue();
100    
101                                    Assert.fail(
102                                            "Thread " + thread + " caught concurrent failure: " +
103                                                    error);
104    
105                                    throw error;
106                            }
107                    }
108                    finally {
109                            _concurrentFailures.clear();
110                    }
111            }
112    
113            public static List<CaptureAppender> startAssert(
114                    List<ExpectedLogs> expectedLogsList) {
115    
116                    _thread = Thread.currentThread();
117                    _uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
118    
119                    Thread.setDefaultUncaughtExceptionHandler(
120                            new LogAssertionUncaughtExceptionHandler(
121                                    _uncaughtExceptionHandler));
122    
123                    List<CaptureAppender> captureAppenders = new ArrayList<>(
124                            expectedLogsList.size());
125    
126                    for (ExpectedLogs expectedLogs : expectedLogsList) {
127                            Class<?> clazz = expectedLogs.loggerClass();
128    
129                            captureAppenders.add(
130                                    Log4JLoggerTestUtil.configureLog4JLogger(
131                                            clazz.getName(), Level.toLevel(expectedLogs.level())));
132                    }
133    
134                    installJdk14Handler();
135                    installLog4jAppender();
136    
137                    return captureAppenders;
138            }
139    
140            @Override
141            public void afterClass(
142                    Description description, List<CaptureAppender> captureAppenders) {
143    
144                    ExpectedMultipleLogs expectedMultipleLogs = description.getAnnotation(
145                            ExpectedMultipleLogs.class);
146    
147                    List<ExpectedLogs> expectedLogsList = new ArrayList<>();
148    
149                    if (expectedMultipleLogs == null) {
150                            ExpectedLogs expectedLogs = description.getAnnotation(
151                                    ExpectedLogs.class);
152    
153                            if (expectedLogs != null) {
154                                    expectedLogsList.add(expectedLogs);
155                            }
156                    }
157                    else {
158                            expectedLogsList.addAll(
159                                    Arrays.asList(expectedMultipleLogs.expectedMultipleLogs()));
160                    }
161    
162                    endAssert(expectedLogsList, captureAppenders);
163            }
164    
165            @Override
166            public void afterMethod(
167                    Description description, List<CaptureAppender> captureAppenders,
168                    Object target) {
169    
170                    afterClass(description, captureAppenders);
171            }
172    
173            @Override
174            public List<CaptureAppender> beforeClass(Description description) {
175                    ExpectedMultipleLogs expectedMultipleLogs = description.getAnnotation(
176                            ExpectedMultipleLogs.class);
177    
178                    List<ExpectedLogs> expectedLogsList = new ArrayList<>();
179    
180                    if (expectedMultipleLogs == null) {
181                            ExpectedLogs expectedLogs = description.getAnnotation(
182                                    ExpectedLogs.class);
183    
184                            if (expectedLogs != null) {
185                                    expectedLogsList.add(expectedLogs);
186                            }
187                    }
188                    else {
189                            expectedLogsList.addAll(
190                                    Arrays.asList(expectedMultipleLogs.expectedMultipleLogs()));
191                    }
192    
193                    return startAssert(expectedLogsList);
194            }
195    
196            @Override
197            public List<CaptureAppender> beforeMethod(
198                    Description description, Object target) {
199    
200                    return beforeClass(description);
201            }
202    
203            protected static void installJdk14Handler() {
204                    Logger logger = Logger.getLogger(StringPool.BLANK);
205    
206                    logger.removeHandler(LogAssertionHandler.INSTANCE);
207    
208                    logger.addHandler(LogAssertionHandler.INSTANCE);
209            }
210    
211            protected static void installLog4jAppender() {
212                    org.apache.log4j.Logger logger =
213                            org.apache.log4j.Logger.getRootLogger();
214    
215                    logger.removeAppender(LogAssertionAppender.INSTANCE);
216    
217                    logger.addAppender(LogAssertionAppender.INSTANCE);
218            }
219    
220            protected static boolean isExpected(
221                    List<ExpectedLogs> expectedLogsList, String renderedMessage) {
222    
223                    for (ExpectedLogs expectedLogs : expectedLogsList) {
224                            for (ExpectedLog expectedLog : expectedLogs.expectedLogs()) {
225                                    ExpectedDBType expectedDBType = expectedLog.expectedDBType();
226    
227                                    if (expectedDBType != ExpectedDBType.NONE) {
228                                            DB db = DBManagerUtil.getDB();
229    
230                                            if (expectedDBType.getDBType() != db.getDBType()) {
231                                                    continue;
232                                            }
233                                    }
234    
235                                    ExpectedType expectedType = expectedLog.expectedType();
236    
237                                    if (expectedType == ExpectedType.CONTAINS) {
238                                            if (renderedMessage.contains(expectedLog.expectedLog())) {
239                                                    return true;
240                                            }
241                                    }
242                                    else if (expectedType == ExpectedType.EXACT) {
243                                            if (renderedMessage.equals(expectedLog.expectedLog())) {
244                                                    return true;
245                                            }
246                                    }
247                                    else if (expectedType == ExpectedType.POSTFIX) {
248                                            if (renderedMessage.endsWith(expectedLog.expectedLog())) {
249                                                    return true;
250                                            }
251                                    }
252                                    else if (expectedType == ExpectedType.PREFIX) {
253                                            if (renderedMessage.startsWith(expectedLog.expectedLog())) {
254                                                    return true;
255                                            }
256                                    }
257                            }
258                    }
259    
260                    return false;
261            }
262    
263            private LogAssertionTestCallback() {
264            }
265    
266            private static final Map<Thread, Error> _concurrentFailures =
267                    new ConcurrentHashMap<>();
268            private static volatile Thread _thread;
269            private static volatile UncaughtExceptionHandler _uncaughtExceptionHandler;
270    
271    }