package org.ovirt.engine.core.utils.log;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import org.junit.Test;
import org.ovirt.engine.core.utils.log.Logged.LogLevel;
import org.slf4j.Logger;
/**
* Tests for the {@link LoggedUtils} class.
*/
public class LoggedUtilsTest {
/* --- Types used for testing purposes --- */
/**
* Interface used for DRY testing of {@link LoggedUtils#log}.
*/
private static interface LogSetup {
public void setup(Logger mock, String message, Object... args);
public void verifyCall(Logger mock, String message, Object... args);
}
@Logged(executionLevel = LogLevel.OFF, errorLevel = LogLevel.OFF)
public class LoggedClass {
// Intentionally empty - a stub class for testing
}
public class LoggedSubclass extends LoggedClass {
// Intentionally empty - a stub class for testing
}
@Logged(executionLevel = LogLevel.DEBUG, errorLevel = LogLevel.WARN,
parametersLevel = LogLevel.FATAL, returnLevel = LogLevel.FATAL)
public class LoggedOverridingSubclass extends LoggedClass {
// Intentionally empty - a stub class for testing
}
@Logged(parametersLevel = LogLevel.DEBUG)
public class LoggedOverridingSubclassNoParameters extends LoggedClass {
// Intentionally empty - a stub class for testing
}
@Logged(returnLevel = LogLevel.DEBUG)
public class LoggedOverridingSubclassNoReturn extends LoggedClass {
// Intentionally empty - a stub class for testing
}
/* --- Tests for the method "getObjectId" --- */
@Test
public void testGetObjectIdFromNull() throws Exception {
assertEquals("0", LoggedUtils.getObjectId(null));
}
@Test
public void testGetObjectIdUniqueForDifferentObjects() throws Exception {
assertTrue(!LoggedUtils.getObjectId(new Object()).equals(LoggedUtils.getObjectId(new Object())));
}
@Test
public void testGetObjectIdConsistentForSameObject() throws Exception {
Object obj = new Object();
assertEquals(LoggedUtils.getObjectId(obj), LoggedUtils.getObjectId(obj));
}
/* --- Tests for the method "getAnnotation" --- */
@Test
public void testGetAnnotationFromNull() throws Exception {
assertNull(LoggedUtils.getAnnotation(null));
}
@Test
public void testGetAnnotationFromClass() throws Exception {
Logged logged = LoggedUtils.getAnnotation(new LoggedClass());
assertEquals(LogLevel.OFF, logged.executionLevel());
assertEquals(LogLevel.OFF, logged.errorLevel());
assertEquals(LogLevel.INFO, logged.parametersLevel());
assertEquals(LogLevel.INFO, logged.returnLevel());
}
@Test
public void testGetAnnotationFromSubclass() throws Exception {
assertSame(LoggedUtils.getAnnotation(new LoggedClass()), LoggedUtils.getAnnotation(new LoggedSubclass()));
}
@Test
public void testGetAnnotationFromOverridingSubclass() throws Exception {
Logged logged = LoggedUtils.getAnnotation(new LoggedOverridingSubclass());
assertEquals(LogLevel.DEBUG, logged.executionLevel());
assertEquals(LogLevel.WARN, logged.errorLevel());
assertEquals(LogLevel.FATAL, logged.parametersLevel());
assertEquals(LogLevel.FATAL, logged.returnLevel());
}
/* --- Tests for the method "determineMessage" --- */
@Test
public void testDetermineMessageReturnsObjectForParameterExpansion() throws Exception {
Object obj = new Object();
Logger log = mock(Logger.class);
when(log.isDebugEnabled()).thenReturn(true);
assertSame(obj,
LoggedUtils.determineMessage(log, LoggedOverridingSubclass.class.getAnnotation(Logged.class), obj));
}
@Test
public void testDetermineMessageReturnsClassNameForNoParameterExpansion() throws Exception {
Logger log = mock(Logger.class);
assertEquals("LoggedUtils.determineMessage shouldn't return parameter expansion for a disabled log level.",
Object.class.getName(),
LoggedUtils.determineMessage(log,
LoggedOverridingSubclassNoParameters.class.getAnnotation(Logged.class), new Object()));
assertEquals("LoggedUtils.determineMessage shouldn't return parameter expansion when diabled completely.",
Object.class.getName(),
LoggedUtils.determineMessage(log, LoggedClass.class.getAnnotation(Logged.class), new Object()));
}
/* --- Tests for the method "log" --- */
@Test
public void testLogOffDoesntLog() {
Logger log = mock(Logger.class);
LoggedUtils.log(log, LogLevel.OFF, "{}", new Object());
verifyZeroInteractions(log);
}
@Test
public void testLogTrace() throws Exception {
helpTestLog(LogLevel.TRACE, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isTraceEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).trace(message, args);
}
});
}
@Test
public void testLogDebug() throws Exception {
helpTestLog(LogLevel.DEBUG, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isDebugEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).debug(message, args);
}
});
}
@Test
public void testLogInfo() throws Exception {
helpTestLog(LogLevel.INFO, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isInfoEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).info(message, args);
}
});
}
@Test
public void testLogWarn() throws Exception {
helpTestLog(LogLevel.WARN, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isWarnEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).warn(message, args);
}
});
}
@Test
public void testLogError() throws Exception {
helpTestLog(LogLevel.ERROR, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isErrorEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).error(message, args);
}
});
}
@Test
public void testLogFatal() throws Exception {
helpTestLog(LogLevel.FATAL, new LogSetup() {
@Override
public void setup(Logger mock, String message, Object... args) {
when(mock.isErrorEnabled()).thenReturn(true);
}
@Override
public void verifyCall(Logger mock, String message, Object... args) {
verify(mock).error(message, args);
}
});
}
/* --- Tests for the method "logEntry" --- */
@Test
public void testLogEntryDoesntLogWhenNoAnnotation() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logEntry(log, "", new Object());
verifyZeroInteractions(log);
}
@Test
public void testLogEntryDoesntLogWhenLogLevelInactive() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logEntry(log, "", new LoggedOverridingSubclass());
verifyNoLogging(log);
}
@Test
public void testLogEntryLogsWhenLogLevelActive() throws Exception {
String id = "";
Logger log = mock(Logger.class);
when(log.isDebugEnabled()).thenReturn(true);
LoggedUtils.logEntry(log, id, new LoggedOverridingSubclass());
verify(log).debug(eq(LoggedUtils.ENTRY_LOG), new Object[] {any(), eq(id)});
}
/* --- Tests for the method "logReturn" --- */
@Test
public void testLogReturnDoesntLogWhenNoAnnotation() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logReturn(log, "", new Object(), new Object());
verifyZeroInteractions(log);
}
@Test
public void testLogReturnDoesntLogWhenLogLevelInactive() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logReturn(log, "", new LoggedOverridingSubclass(), new Object());
verifyNoLogging(log);
}
@Test
public void testLogReturnLogsWhenLogLevelActiveAndNoExpandReturn() throws Exception {
String id = "";
Logger log = mock(Logger.class);
when(log.isInfoEnabled()).thenReturn(true);
LoggedUtils.logReturn(log, id, new LoggedOverridingSubclassNoReturn(), new Object());
verify(log).info(eq(LoggedUtils.EXIT_LOG_VOID), new Object[] {any(), eq(id)});
}
@Test
public void testLogReturnLogsWhenLogLevelActiveAndExpandReturn() throws Exception {
String id = "";
Logger log = mock(Logger.class);
when(log.isDebugEnabled()).thenReturn(true);
LoggedUtils.logReturn(log, id, new LoggedOverridingSubclass(), new Object());
verify(log).debug(eq(LoggedUtils.EXIT_LOG_RETURN_VALUE), any(), any(), eq(id));
}
@Test
public void testLogReturnLogsWhenLogLevelActiveAndExpandReturnButNullReturn() throws Exception {
String id = "";
Logger log = mock(Logger.class);
when(log.isDebugEnabled()).thenReturn(true);
LoggedUtils.logReturn(log, id, new LoggedOverridingSubclass(), null);
verify(log).debug(eq(LoggedUtils.EXIT_LOG_VOID), new Object[] {any(), eq("")});
}
/* --- Tests for the method "logError" --- */
@Test
public void testLogErrorDoesntLogWhenNoAnnotation() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logError(log, "", new Object(), new Exception());
verifyZeroInteractions(log);
}
@Test
public void testLogErrorDoesntLogWhenLogLevelInactive() throws Exception {
Logger log = mock(Logger.class);
LoggedUtils.logError(log, "", new LoggedOverridingSubclass(), new Exception());
verifyNoLogging(log);
}
@Test
public void testLogErrorLogsWhenLogLevelActive() throws Exception {
String id = "";
Logger log = mock(Logger.class);
// when the call to determine whether to log the parameters or not.
when(log.isDebugEnabled()).thenReturn(true);
when(log.isWarnEnabled()).thenReturn(true);
Exception e = new Exception();
LoggedUtils.logError(log, id, new LoggedOverridingSubclass(), e);
verify(log).warn(eq(LoggedUtils.ERROR_LOG), any(), any(), eq(id));
verify(log).error(eq("Exception"), eq(e));
}
/* --- Helper methods --- */
/**
* Verifies that no logging was done on the given log mock.
*/
private static void verifyNoLogging(Logger logMock) {
verify(logMock, never()).trace(anyString(), (Object) any());
verify(logMock, never()).debug(anyString(), (Object) any());
verify(logMock, never()).info(anyString(), (Object) any());
verify(logMock, never()).warn(anyString(), (Object) any());
verify(logMock, never()).error(anyString(), (Object) any());
}
/**
* Helper method to test the {@link LoggedUtils#log} method functionality.
* @param logLevel Log level to test.
* @param logSetup Setup the mocks using an anonymous inner class of this interface.
*/
private static void helpTestLog(LogLevel logLevel, LogSetup logSetup) {
Logger log = mock(Logger.class);
String message = "{}";
Object s = "arg1";
logSetup.setup(log, message, s);
LoggedUtils.log(log, logLevel, message, s);
logSetup.verifyCall(log, message, s);
}
}