@@ -212,6 +212,7 @@ subprojects { | |||
dependency 'javax.servlet:javax.servlet-api:3.1.0' | |||
dependency 'javax.xml.bind:jaxb-api:2.3.0' | |||
dependency 'junit:junit:4.12' | |||
dependency 'org.junit.jupiter:junit-jupiter-api:5.5.2' | |||
dependency 'net.jpountz.lz4:lz4:1.3.0' | |||
dependency 'net.lightbody.bmp:littleproxy:1.1.0-beta-bmp-17' | |||
dependency 'org.awaitility:awaitility:4.0.1' |
@@ -25,6 +25,8 @@ dependencies { | |||
compileOnly 'com.google.code.findbugs:jsr305' | |||
compileOnly 'javax.servlet:javax.servlet-api' | |||
compileOnly 'junit:junit' | |||
// Used by LogTesterJUnit5 | |||
compileOnly 'org.junit.jupiter:junit-jupiter-api' | |||
compileOnly 'org.slf4j:slf4j-api' | |||
testCompile 'com.google.guava:guava' |
@@ -0,0 +1,84 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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 3 of the License, or (at your option) any later version. | |||
* | |||
* This program 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. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.api.utils.log; | |||
import java.util.List; | |||
class AbstractLogTester { | |||
protected void before() { | |||
// this shared instance breaks compatibility with parallel execution of tests | |||
LogInterceptors.set(new ListInterceptor()); | |||
setLevel(LoggerLevel.INFO); | |||
} | |||
protected void after() { | |||
LogInterceptors.set(NullInterceptor.NULL_INSTANCE); | |||
setLevel(LoggerLevel.INFO); | |||
} | |||
LoggerLevel getLevel() { | |||
return Loggers.getFactory().getLevel(); | |||
} | |||
/** | |||
* Enable/disable debug logs. Info, warn and error logs are always enabled. | |||
* By default INFO logs are enabled when LogTester is started. | |||
*/ | |||
public AbstractLogTester setLevel(LoggerLevel level) { | |||
Loggers.getFactory().setLevel(level); | |||
return this; | |||
} | |||
/** | |||
* Logs in chronological order (item at index 0 is the oldest one) | |||
*/ | |||
public List<String> logs() { | |||
return ((ListInterceptor) LogInterceptors.get()).logs(); | |||
} | |||
/** | |||
* Logs in chronological order (item at index 0 is the oldest one) for | |||
* a given level | |||
*/ | |||
public List<String> logs(LoggerLevel level) { | |||
return ((ListInterceptor) LogInterceptors.get()).logs(level); | |||
} | |||
/** | |||
* Logs with arguments in chronological order (item at index 0 is the oldest one) | |||
*/ | |||
public List<LogAndArguments> getLogs() { | |||
return ((ListInterceptor) LogInterceptors.get()).getLogs(); | |||
} | |||
/** | |||
* Logs with arguments in chronological order (item at index 0 is the oldest one) for | |||
* a given level | |||
*/ | |||
public List<LogAndArguments> getLogs(LoggerLevel level) { | |||
return ((ListInterceptor) LogInterceptors.get()).getLogs(level); | |||
} | |||
public AbstractLogTester clear() { | |||
((ListInterceptor) LogInterceptors.get()).clear(); | |||
return this; | |||
} | |||
} |
@@ -19,8 +19,9 @@ | |||
*/ | |||
package org.sonar.api.utils.log; | |||
import java.util.List; | |||
import org.junit.rules.ExternalResource; | |||
import org.junit.rules.TestRule; | |||
import org.junit.runner.Description; | |||
import org.junit.runners.model.Statement; | |||
/** | |||
* <b>For tests only</b> | |||
@@ -55,66 +56,22 @@ import org.junit.rules.ExternalResource; | |||
* | |||
* @since 5.1 | |||
*/ | |||
public class LogTester extends ExternalResource { | |||
@Override | |||
protected void before() throws Throwable { | |||
// this shared instance breaks compatibility with parallel execution of tests | |||
LogInterceptors.set(new ListInterceptor()); | |||
setLevel(LoggerLevel.INFO); | |||
} | |||
@Override | |||
protected void after() { | |||
LogInterceptors.set(NullInterceptor.NULL_INSTANCE); | |||
setLevel(LoggerLevel.INFO); | |||
} | |||
LoggerLevel getLevel() { | |||
return Loggers.getFactory().getLevel(); | |||
} | |||
/** | |||
* Enable/disable debug logs. Info, warn and error logs are always enabled. | |||
* By default INFO logs are enabled when LogTester is started. | |||
*/ | |||
public LogTester setLevel(LoggerLevel level) { | |||
Loggers.getFactory().setLevel(level); | |||
return this; | |||
} | |||
/** | |||
* Logs in chronological order (item at index 0 is the oldest one) | |||
*/ | |||
public List<String> logs() { | |||
return ((ListInterceptor) LogInterceptors.get()).logs(); | |||
} | |||
/** | |||
* Logs in chronological order (item at index 0 is the oldest one) for | |||
* a given level | |||
*/ | |||
public List<String> logs(LoggerLevel level) { | |||
return ((ListInterceptor) LogInterceptors.get()).logs(level); | |||
} | |||
/** | |||
* Logs with arguments in chronological order (item at index 0 is the oldest one) | |||
*/ | |||
public List<LogAndArguments> getLogs() { | |||
return ((ListInterceptor) LogInterceptors.get()).getLogs(); | |||
} | |||
/** | |||
* Logs with arguments in chronological order (item at index 0 is the oldest one) for | |||
* a given level | |||
*/ | |||
public List<LogAndArguments> getLogs(LoggerLevel level) { | |||
return ((ListInterceptor) LogInterceptors.get()).getLogs(level); | |||
public class LogTester extends AbstractLogTester implements TestRule { | |||
public Statement apply(Statement base, Description description) { | |||
return statement(base); | |||
} | |||
public LogTester clear() { | |||
((ListInterceptor) LogInterceptors.get()).clear(); | |||
return this; | |||
private Statement statement(final Statement base) { | |||
return new Statement() { | |||
@Override | |||
public void evaluate() throws Throwable { | |||
before(); | |||
try { | |||
base.evaluate(); | |||
} finally { | |||
after(); | |||
} | |||
} | |||
}; | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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 3 of the License, or (at your option) any later version. | |||
* | |||
* This program 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. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.api.utils.log; | |||
import org.junit.jupiter.api.extension.AfterTestExecutionCallback; | |||
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; | |||
import org.junit.jupiter.api.extension.ExtensionContext; | |||
/** | |||
* <b>For tests only</b> | |||
* <br> | |||
* This JUnit 5 extension allows to configure and access logs in tests. By default | |||
* trace level is enabled. | |||
* <br> | |||
* Warning - not compatible with parallel execution of tests in the same JVM fork. | |||
* <br> | |||
* Example: | |||
* <pre> | |||
* public class MyClass { | |||
* private final Logger logger = Loggers.get("logger_name"); | |||
* | |||
* public void doSomething() { | |||
* logger.info("foo"); | |||
* } | |||
* } | |||
* | |||
* class MyClassTests { | |||
* @org.junit.jupiter.api.extension.RegisterExtension | |||
* LogTesterJUnit5 logTester = new LogTesterJUnit5(); | |||
* | |||
* @org.junit.jupiter.api.Test | |||
* public void test_log() { | |||
* new MyClass().doSomething(); | |||
* | |||
* assertThat(logTester.logs()).containsOnly("foo"); | |||
* } | |||
* } | |||
* </pre> | |||
* | |||
* @since 8.1 | |||
*/ | |||
public class LogTesterJUnit5 extends AbstractLogTester implements BeforeTestExecutionCallback, AfterTestExecutionCallback { | |||
@Override | |||
public void beforeTestExecution(ExtensionContext context) throws Exception { | |||
before(); | |||
} | |||
@Override | |||
public void afterTestExecution(ExtensionContext context) throws Exception { | |||
after(); | |||
} | |||
} |
@@ -0,0 +1,119 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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 3 of the License, or (at your option) any later version. | |||
* | |||
* This program 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. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.api.utils.log; | |||
import java.util.concurrent.atomic.AtomicBoolean; | |||
import org.junit.Test; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class LogTesterJUnit5Test { | |||
LogTesterJUnit5 underTest = new LogTesterJUnit5(); | |||
@Test | |||
public void info_level_by_default() throws Throwable { | |||
// when LogTester is used, then info logs are enabled by default | |||
underTest.beforeTestExecution(null); | |||
assertThat(underTest.getLevel()).isEqualTo(LoggerLevel.INFO); | |||
assertThat(Loggers.getFactory().getLevel()).isEqualTo(LoggerLevel.INFO); | |||
// change | |||
underTest.setLevel(LoggerLevel.DEBUG); | |||
assertThat(underTest.getLevel()).isEqualTo(LoggerLevel.DEBUG); | |||
assertThat(Loggers.getFactory().getLevel()).isEqualTo(LoggerLevel.DEBUG); | |||
// reset to initial level after execution of test | |||
underTest.afterTestExecution(null); | |||
assertThat(underTest.getLevel()).isEqualTo(LoggerLevel.INFO); | |||
assertThat(Loggers.getFactory().getLevel()).isEqualTo(LoggerLevel.INFO); | |||
} | |||
@Test | |||
public void intercept_logs() throws Throwable { | |||
underTest.beforeTestExecution(null); | |||
Loggers.get("logger1").info("an information"); | |||
Loggers.get("logger2").warn("warning: {}", 42); | |||
assertThat(underTest.logs()).containsExactly("an information", "warning: 42"); | |||
assertThat(underTest.logs(LoggerLevel.ERROR)).isEmpty(); | |||
assertThat(underTest.logs(LoggerLevel.INFO)).containsOnly("an information"); | |||
assertThat(underTest.logs(LoggerLevel.WARN)).containsOnly("warning: 42"); | |||
underTest.clear(); | |||
assertThat(underTest.logs()).isEmpty(); | |||
assertThat(underTest.logs(LoggerLevel.INFO)).isEmpty(); | |||
underTest.afterTestExecution(null); | |||
assertThat(LogInterceptors.get()).isSameAs(NullInterceptor.NULL_INSTANCE); | |||
} | |||
@Test | |||
public void use_suppliers() throws Throwable { | |||
// when LogTester is used, then info logs are enabled by default | |||
underTest.beforeTestExecution(null); | |||
AtomicBoolean touchedTrace = new AtomicBoolean(); | |||
AtomicBoolean touchedDebug = new AtomicBoolean(); | |||
Loggers.get("logger1").trace(() -> { | |||
touchedTrace.set(true); | |||
return "a trace information"; | |||
}); | |||
Loggers.get("logger1").debug(() -> { | |||
touchedDebug.set(true); | |||
return "a debug information"; | |||
}); | |||
assertThat(underTest.logs()).isEmpty(); | |||
assertThat(touchedTrace.get()).isFalse(); | |||
assertThat(touchedDebug.get()).isFalse(); | |||
// change level to DEBUG | |||
underTest.setLevel(LoggerLevel.DEBUG); | |||
Loggers.get("logger1").trace(() -> { | |||
touchedTrace.set(true); | |||
return "a trace information"; | |||
}); | |||
Loggers.get("logger1").debug(() -> { | |||
touchedDebug.set(true); | |||
return "a debug information"; | |||
}); | |||
assertThat(underTest.logs()).containsOnly("a debug information"); | |||
assertThat(touchedTrace.get()).isFalse(); | |||
assertThat(touchedDebug.get()).isTrue(); | |||
touchedDebug.set(false); | |||
underTest.clear(); | |||
// change level to TRACE | |||
underTest.setLevel(LoggerLevel.TRACE); | |||
Loggers.get("logger1").trace(() -> { | |||
touchedTrace.set(true); | |||
return "a trace information"; | |||
}); | |||
Loggers.get("logger1").debug(() -> { | |||
touchedDebug.set(true); | |||
return "a debug information"; | |||
}); | |||
assertThat(underTest.logs()).containsExactly("a trace information", "a debug information"); | |||
assertThat(touchedTrace.get()).isTrue(); | |||
assertThat(touchedDebug.get()).isTrue(); | |||
} | |||
} |