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.test.rule.callback.BaseTestCallback;
018    import com.liferay.portal.kernel.util.StringPool;
019    import com.liferay.portal.test.log.CaptureAppender;
020    import com.liferay.portal.test.log.Log4JLoggerTestUtil;
021    import com.liferay.portal.test.rule.ExpectedLog;
022    import com.liferay.portal.test.rule.ExpectedLogs;
023    import com.liferay.portal.test.rule.ExpectedType;
024    import com.liferay.portal.test.rule.LogAssertionAppender;
025    import com.liferay.portal.test.rule.LogAssertionHandler;
026    
027    import java.util.Map;
028    import java.util.concurrent.ConcurrentHashMap;
029    import java.util.logging.Logger;
030    
031    import org.apache.log4j.Level;
032    import org.apache.log4j.spi.LoggingEvent;
033    
034    import org.junit.Assert;
035    import org.junit.runner.Description;
036    
037    /**
038     * @author Shuyang Zhou
039     */
040    public class LogAssertionTestCallback
041            extends BaseTestCallback<CaptureAppender, CaptureAppender> {
042    
043            public static final LogAssertionTestCallback INSTANCE =
044                    new LogAssertionTestCallback();
045    
046            public static void caughtFailure(Error error) {
047                    Thread currentThread = Thread.currentThread();
048    
049                    if (currentThread != _thread) {
050                            _concurrentFailures.put(currentThread, error);
051    
052                            _thread.interrupt();
053                    }
054                    else {
055                            throw error;
056                    }
057            }
058    
059            public static void endAssert(
060                    ExpectedLogs expectedLogs, CaptureAppender captureAppender) {
061    
062                    if (expectedLogs != null) {
063                            try {
064                                    for (LoggingEvent loggingEvent :
065                                                    captureAppender.getLoggingEvents()) {
066    
067                                            String renderedMessage = loggingEvent.getRenderedMessage();
068    
069                                            if (!isExpected(expectedLogs, renderedMessage)) {
070                                                    Assert.fail(renderedMessage);
071                                            }
072                                    }
073                            }
074                            finally {
075                                    captureAppender.close();
076                            }
077                    }
078    
079                    _thread = null;
080    
081                    try {
082                            for (Map.Entry<Thread, Error> entry :
083                                            _concurrentFailures.entrySet()) {
084    
085                                    Thread thread = entry.getKey();
086                                    Error error = entry.getValue();
087    
088                                    Assert.fail(
089                                            "Thread " + thread + " caught concurrent failure: " +
090                                                    error);
091    
092                                    throw error;
093                            }
094                    }
095                    finally {
096                            _concurrentFailures.clear();
097                    }
098            }
099    
100            public static CaptureAppender startAssert(ExpectedLogs expectedLogs) {
101                    _thread = Thread.currentThread();
102    
103                    CaptureAppender captureAppender = null;
104    
105                    if (expectedLogs != null) {
106                            Class<?> clazz = expectedLogs.loggerClass();
107    
108                            captureAppender = Log4JLoggerTestUtil.configureLog4JLogger(
109                                    clazz.getName(), Level.toLevel(expectedLogs.level()));
110                    }
111    
112                    installJdk14Handler();
113                    installLog4jAppender();
114    
115                    return captureAppender;
116            }
117    
118            @Override
119            public void doAfterClass(
120                    Description description, CaptureAppender captureAppender) {
121    
122                    ExpectedLogs expectedLogs = description.getAnnotation(
123                            ExpectedLogs.class);
124    
125                    endAssert(expectedLogs, captureAppender);
126            }
127    
128            @Override
129            public void doAfterMethod(
130                    Description description, CaptureAppender captureAppender,
131                    Object target) {
132    
133                    doAfterClass(description, captureAppender);
134            }
135    
136            @Override
137            public CaptureAppender doBeforeClass(Description description) {
138                    ExpectedLogs expectedLogs = description.getAnnotation(
139                            ExpectedLogs.class);
140    
141                    return startAssert(expectedLogs);
142            }
143    
144            @Override
145            public CaptureAppender doBeforeMethod(
146                    Description description, Object target) {
147    
148                    return doBeforeClass(description);
149            }
150    
151            protected static void installJdk14Handler() {
152                    Logger logger = Logger.getLogger(StringPool.BLANK);
153    
154                    logger.removeHandler(LogAssertionHandler.INSTANCE);
155    
156                    logger.addHandler(LogAssertionHandler.INSTANCE);
157            }
158    
159            protected static void installLog4jAppender() {
160                    org.apache.log4j.Logger logger =
161                            org.apache.log4j.Logger.getRootLogger();
162    
163                    logger.removeAppender(LogAssertionAppender.INSTANCE);
164    
165                    logger.addAppender(LogAssertionAppender.INSTANCE);
166            }
167    
168            protected static boolean isExpected(
169                    ExpectedLogs expectedLogs, String renderedMessage) {
170    
171                    for (ExpectedLog expectedLog : expectedLogs.expectedLogs()) {
172                            ExpectedType expectedType = expectedLog.expectedType();
173    
174                            if (expectedType == ExpectedType.EXACT) {
175                                    if (renderedMessage.equals(expectedLog.expectedLog())) {
176                                            return true;
177                                    }
178                            }
179                            else if (expectedType == ExpectedType.POSTFIX) {
180                                    if (renderedMessage.endsWith(expectedLog.expectedLog())) {
181                                            return true;
182                                    }
183                            }
184                            else if (expectedType == ExpectedType.PREFIX) {
185                                    if (renderedMessage.startsWith(expectedLog.expectedLog())) {
186                                            return true;
187                                    }
188                            }
189                    }
190    
191                    return false;
192            }
193    
194            private LogAssertionTestCallback() {
195            }
196    
197            private static final Map<Thread, Error> _concurrentFailures =
198                    new ConcurrentHashMap<>();
199            private static volatile Thread _thread;
200    
201    }