diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9dda96..7f5290a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## Unreleased changes
+## 0.1.0 - 2020-08-30
### Added
- TestExecutionListener that discards all log events related to passing tests
\ No newline at end of file
diff --git a/README.md b/README.md
index 18de891..32d10d5 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Junit5 test execution listener that hides log events for "green" tests.
## Rationale
Have you ever faced with the logging noise during your project build? Maybe you had opportunity to download tens
-megabytes of log to analyze issue just because some chatty package has enabled DEBUG logging "in case of something goes wrong"?
+megabytes of log to analyze issue just because DEBUG level was enabled on some chatty package "in case of something goes wrong"?
Big chance you did, if your project uses some logging framework (like slf4j), and you write unit tests.
The reason is simple - logs allow you to get extra information if something goes wrong,
@@ -39,6 +39,23 @@ engine, it will work as well.
Right now only Logback is supported, although it should be possible to add support for other frameworks as well (e.g. log4j2)
in case of need and request from community.
+### Configuration
+Default behavior for listener is to work only when running via build system (e.g. maven) execution and do nothing with logs
+when executed from IDE's run-test option. This provides reasonable balance between log amount and usability - IDE is usually
+smart enough to provide good navigation in logs and show only relevant info.
+
+This can be adjusted with defining configuration properties in the following ways (in the order of priority):
+1. `reasonable-test-logs.properties` in root of classpath
+2. System properties (one specified with `-D` key)
+3. Environment variables
+
+| Property name | Env variable format | Description |
+|---------------|---------------------|-------------|
+| `rtlogs.mode` | `RTLOGS_MODE` | Type of listener to use. Possible options:
`auto` - "default" mode that disables logic in IDE and enables in regular build system run.
`nop` - explicit NOP mode (IDE mode)
`reasonable` - explicit logs processing mode (always build system) |
+| `rtlogs.debug` | `RTLOGS_DEBUG` | Used in NOP mode for listener debugging purposes |
+
+### Limitations
+It is assumed that tests are executed in sequential manner. Parallel test execution support was not researched so far.
## Examples
@@ -46,6 +63,10 @@ Check `example` directory for various test-samples. The simplest way to run it -
```
mvn clean package
```
+Take a look at an amount of logs and then compare it to the mode when listener is disabled:
+```
+mvn clean package -Drtlogs.mode=nop
+```
## Roadmap
@@ -61,4 +82,18 @@ in build system (e.g. surefire params for junit5).
Anyway, some techniques published on [github](https://github.com/nt-ca-aqe/testit-testutils/tree/master/testutils-logsuppressor-logback)
were useful.
+
+# Development
+
+## Release
+
+1. Make sure your `~/.m2/settings.xml` contains
+ - profile with property `gpg.keyname` valid gpg key id
+ - server definition with id `ossrh` with valid credentials to nexus oss repo
+2. Upload of artifacts is as simple as:
+```bash
+cd reasonable-test-logs
+mvn clean deploy -Prelease
+```
+
\ No newline at end of file
diff --git a/example/pom.xml b/example/pom.xml
index 575029a..7f39084 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -22,18 +22,13 @@
com.github.zeldigas
reasonable-test-logs
- 1.0-SNAPSHOT
-
-
-
- org.junit.jupiter
- junit-jupiter-api
- 5.5.2
+ 0.1.0
test
+
org.junit.jupiter
- junit-jupiter-engine
+ junit-jupiter
5.5.2
test
@@ -49,6 +44,12 @@
1.2.3
test
+
+ org.junit.platform
+ junit-platform-launcher
+ 1.5.2
+ test
+
@@ -56,7 +57,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.0.0-M3
+ 2.22.2
true
diff --git a/reasonable-test-logs/pom.xml b/reasonable-test-logs/pom.xml
index 1e6e9b4..f8d3a95 100644
--- a/reasonable-test-logs/pom.xml
+++ b/reasonable-test-logs/pom.xml
@@ -6,18 +6,50 @@
com.github.zeldigas
reasonable-test-logs
- 1.0-SNAPSHOT
+ 0.1.0
1.8
1.8
+ UTF-8
+
+ scm:git:git@github.com:zeldigas/reasonable-test-logs.git
+ scm:git:git@github.com:zeldigas/reasonable-test-logs.git
+ https://github.com/zeldigas/Reasonable-Test-Logs
+
+
+
+
+ MIT License
+ http://www.opensource.org/licenses/mit-license.php
+
+
+
+
+
+ Dmitry Pavlov
+ zeldigas@gmail.com
+
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
org.junit.platform
junit-platform-launcher
- 1.6.2
+ 1.5.2
provided
@@ -26,6 +58,113 @@
1.2.3
provided
+
+
+ org.mockito
+ mockito-core
+ 3.1.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.5.2
+ test
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.2.0
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+ true
+
+
+
+ io.github.zlika
+ reproducible-build-maven-plugin
+ 0.12
+
+
+
+ strip-jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ --pinentry-mode
+ loopback
+
+ ${gpg.keyname}
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/Config.java b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/Config.java
new file mode 100644
index 0000000..311b0a3
--- /dev/null
+++ b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/Config.java
@@ -0,0 +1,96 @@
+package com.github.zeldigas.rtlogs;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+
+public class Config {
+
+ public static final String CONFIG_FILE = "/reasonable-test-logs.properties";
+ private static final String PROPS_PREFIX = "rtlogs.";
+ private static final String PROP_CONTROLLER_TYPE = PROPS_PREFIX + "mode";
+ private static final String PROP_DEBUG = PROPS_PREFIX + "debug";
+
+ enum ControllerType {
+ AUTO, NOP, REASONABLE
+ }
+
+ private final ControllerType controllerType;
+ private final boolean debug;
+
+ public Config(ControllerType controllerType, boolean debug) {
+ this.controllerType = controllerType;
+ this.debug = debug;
+ }
+
+ public ControllerType getControllerType() {
+ return controllerType;
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public static Config load() {
+ return load(CONFIG_FILE);
+ }
+
+ static Config load(String file) {
+ Properties properties = defaultProperties();
+ mergeTo(loadConfigFile(file), properties);
+ mergeTo(System.getProperties(), properties);
+ mergeTo(propertiesFromEnvVars(), properties);
+
+ return new Config(
+ ControllerType.valueOf(properties.getProperty(PROP_CONTROLLER_TYPE).toUpperCase()),
+ toBool(properties.getProperty(PROP_DEBUG))
+ );
+ }
+
+ private static Properties loadConfigFile(String file) {
+ Properties properties = new Properties();
+ InputStream config = Config.class.getResourceAsStream(file);
+ if (config != null) {
+ try {
+ properties.load(config);
+ } catch (Exception e) {
+ System.err.println("Failed to load properties from " + file + " file");
+ e.printStackTrace();
+ }
+ }
+ return properties;
+ }
+
+ private static void mergeTo(Properties source, Properties dest) {
+ source.forEach((k, v) -> {
+ if (k.toString().startsWith(PROPS_PREFIX)) {
+ dest.put(k, v);
+ }
+ });
+ }
+
+ private static boolean toBool(String value) {
+ return Boolean.parseBoolean(value);
+ }
+
+ private static Properties defaultProperties() {
+ Properties properties = new Properties();
+ properties.setProperty(PROP_CONTROLLER_TYPE, "auto");
+ properties.setProperty(PROP_DEBUG, "false");
+ return properties;
+ }
+
+ private static Properties propertiesFromEnvVars() {
+ Properties properties = new Properties();
+ Map envVars = System.getenv();
+ if (envVars.containsKey("RTLOGS_MODE")) {
+ properties.setProperty(PROP_CONTROLLER_TYPE, envVars.get("RTLOGS_MODE"));
+ }
+ if (envVars.containsKey("RTLOGS_DEBUG")) {
+ properties.setProperty(PROP_DEBUG, envVars.get("RTLOGS_DEBUG"));
+ }
+ return properties;
+ }
+
+
+}
diff --git a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/ExecutionEnvironment.java b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/ExecutionEnvironment.java
new file mode 100644
index 0000000..6eff7b8
--- /dev/null
+++ b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/ExecutionEnvironment.java
@@ -0,0 +1,14 @@
+package com.github.zeldigas.rtlogs;
+
+public class ExecutionEnvironment {
+
+ public static boolean ideMode() {
+ String val = System.getProperties().getProperty("sun.java.command");
+ return isRunningInIntellij(val);
+ }
+
+ private static boolean isRunningInIntellij(String val) {
+ return val.startsWith("com.intellij.rt.");
+ }
+
+}
diff --git a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LogExecutionListener.java b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LogExecutionListener.java
index 5687b61..f578f71 100644
--- a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LogExecutionListener.java
+++ b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LogExecutionListener.java
@@ -7,7 +7,15 @@
public class LogExecutionListener implements TestExecutionListener {
- private LoggingController loggingController = new LoggingControllerImpl();
+ private LoggingController loggingController;
+
+ public LogExecutionListener() {
+ this(LoggingController.getInstance());
+ }
+
+ public LogExecutionListener(LoggingController loggingController) {
+ this.loggingController = loggingController;
+ }
public void testPlanExecutionStarted(TestPlan testPlan) {
loggingController.startCapture();
diff --git a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LoggingController.java b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LoggingController.java
index eaf3afa..70f8449 100644
--- a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LoggingController.java
+++ b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/LoggingController.java
@@ -1,6 +1,20 @@
package com.github.zeldigas.rtlogs;
public interface LoggingController {
+ static LoggingController getInstance() {
+ Config config = Config.load();
+
+ Config.ControllerType controllerType = config.getControllerType();
+ if (controllerType == Config.ControllerType.AUTO) {
+ controllerType = ExecutionEnvironment.ideMode() ? Config.ControllerType.NOP : Config.ControllerType.REASONABLE;
+ }
+ switch (controllerType) {
+ case NOP: return new NopLoggingController(config.isDebug());
+ case REASONABLE: return new LoggingControllerImpl();
+ default: throw new IllegalStateException("Invalid controller type");
+ }
+ }
+
void enter(String execution);
void exit(String execution);
diff --git a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/NopLoggingController.java b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/NopLoggingController.java
index c6a5d0c..db706dc 100644
--- a/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/NopLoggingController.java
+++ b/reasonable-test-logs/src/main/java/com/github/zeldigas/rtlogs/NopLoggingController.java
@@ -1,6 +1,13 @@
package com.github.zeldigas.rtlogs;
public class NopLoggingController implements LoggingController {
+
+ private final boolean report;
+
+ public NopLoggingController(boolean report) {
+ this.report = report;
+ }
+
@Override
public void enter(String execution) {
report("Execution started: " + execution);
@@ -27,6 +34,8 @@ public void stopCapture() {
}
private void report(String msg) {
- System.out.println("[" + Thread.currentThread().getName() + "] NopLoggingController: " + msg);
+ if (report) {
+ System.out.println("[" + Thread.currentThread().getName() + "] NopLoggingController: " + msg);
+ }
}
}
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ConfigTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ConfigTest.java
new file mode 100644
index 0000000..79cb0c1
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ConfigTest.java
@@ -0,0 +1,49 @@
+package com.github.zeldigas.rtlogs;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ConfigTest {
+
+ @AfterEach
+ void tearDown() {
+ System.clearProperty("rtlogs.mode");
+ System.clearProperty("rtlogs.debug");
+ }
+
+ @Test
+ void defaultConfig() {
+ Config c = Config.load();//test resources has file with default name
+ assertEquals(Config.ControllerType.NOP, c.getControllerType());
+ assertFalse(c.isDebug());
+ }
+
+ @Test
+ void configIsTakenFromFile() {
+ Config c = Config.load("/rt-reasonable.properties");
+
+ assertEquals(Config.ControllerType.REASONABLE, c.getControllerType());
+ assertTrue(c.isDebug());
+ }
+
+ @Test
+ void invalidConfigIsIgnored() {
+ Config c = Config.load("/bad_file.txt");
+
+ assertEquals(Config.ControllerType.NOP, c.getControllerType());
+ assertFalse(c.isDebug());
+ }
+
+ @Test
+ void systemPropertiesTakesPrecedenceOverConfigurationFile() {
+ System.setProperty("rtlogs.mode", "auto");
+ System.setProperty("rtlogs.debug", "false");
+
+ Config c = Config.load("/rt-reasonable.properties");
+
+ assertEquals(Config.ControllerType.AUTO, c.getControllerType());
+ assertFalse(c.isDebug());
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ExecutionEnvironmentTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ExecutionEnvironmentTest.java
new file mode 100644
index 0000000..3f0b61a
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/ExecutionEnvironmentTest.java
@@ -0,0 +1,35 @@
+package com.github.zeldigas.rtlogs;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ExecutionEnvironmentTest {
+
+ private String preservedPropery;
+
+ @BeforeEach
+ void setUp() {
+ preservedPropery = System.getProperty("sun.java.command");
+ }
+
+ @AfterEach
+ void tearDown() {
+ System.setProperty("sun.java.command", preservedPropery);
+ }
+
+ @Test
+ void noIdeMode() {
+ System.setProperty("sun.java.command", "something else");
+
+ assertFalse(ExecutionEnvironment.ideMode());
+ }
+
+ @Test
+ void ideMode() {
+ System.setProperty("sun.java.command", "com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5");
+ assertTrue(ExecutionEnvironment.ideMode());
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogCollectingAppenderTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogCollectingAppenderTest.java
new file mode 100644
index 0000000..862a31b
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogCollectingAppenderTest.java
@@ -0,0 +1,51 @@
+package com.github.zeldigas.rtlogs;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.mock;
+
+class LogCollectingAppenderTest {
+
+ private LogCollectingAppender appender = new LogCollectingAppender();
+
+ @Test
+ void appenderCollectMessagesToList() {
+ ILoggingEvent event = mock(ILoggingEvent.class);
+
+ appender.doAppend(event);
+
+ assertEquals(1, appender.getEvents().size());
+ assertSame(event, appender.getEvents().get(0));
+ }
+
+ @Test
+ void resettingCollectingEventsToBookmark() {
+ ILoggingEvent event = mock(ILoggingEvent.class);
+ ILoggingEvent event1 = mock(ILoggingEvent.class);
+
+ assertEquals(0, appender.getEventsRef());
+
+ appender.doAppend(event);
+
+ assertEquals(1, appender.getEventsRef());
+
+ appender.doAppend(event1);
+ appender.doAppend(event1);
+
+ assertEquals(3, appender.getEventsRef());
+ assertEquals(Arrays.asList(event, event1, event1), appender.getEvents());
+
+ appender.resetTo(2);
+ assertEquals(2, appender.getEvents().size());
+ assertEquals(Arrays.asList(event, event1), appender.getEvents());
+
+ appender.resetTo(0);
+ assertEquals(0, appender.getEvents().size());
+
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogExecutionListenerTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogExecutionListenerTest.java
new file mode 100644
index 0000000..d1a7b00
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LogExecutionListenerTest.java
@@ -0,0 +1,77 @@
+package com.github.zeldigas.rtlogs;
+
+import org.junit.jupiter.api.Test;
+import org.junit.platform.engine.TestDescriptor;
+import org.junit.platform.engine.TestExecutionResult;
+import org.junit.platform.engine.UniqueId;
+import org.junit.platform.launcher.TestIdentifier;
+import org.junit.platform.launcher.TestPlan;
+
+import java.util.Optional;
+
+import static org.mockito.Mockito.*;
+
+class LogExecutionListenerTest {
+
+ private LoggingController controller = mock(LoggingController.class);
+ private LogExecutionListener listener = new LogExecutionListener(controller);
+
+ @Test
+ void planStartInitiatesCapture() {
+ listener.testPlanExecutionStarted(mock(TestPlan.class));
+
+ verify(controller).startCapture();
+ }
+
+ @Test
+ void planEndStopsCapture() {
+ listener.testPlanExecutionFinished(mock(TestPlan.class));
+
+ verify(controller).stopCapture();
+ }
+
+ @Test
+ void startExecutionEntersFrame() {
+ TestIdentifier identifier = testIdentifier();
+
+ listener.executionStarted(identifier);
+
+ verify(controller).enter("id");
+ }
+
+ @Test
+ void endExecutionExitsFrameForSuccess() {
+ TestIdentifier identifier = testIdentifier();
+
+ TestExecutionResult result = mock(TestExecutionResult.class);
+ when(result.getStatus()).thenReturn(TestExecutionResult.Status.SUCCESSFUL);
+
+ listener.executionFinished(identifier, result);
+
+ verify(controller).exit("id");
+ }
+
+ @Test
+ void endExecutionExitsFrameForFailure() {
+ TestIdentifier identifier = testIdentifier();
+
+ TestExecutionResult result = mock(TestExecutionResult.class);
+ when(result.getStatus()).thenReturn(TestExecutionResult.Status.FAILED);
+
+ listener.executionFinished(identifier, result);
+
+ verify(controller).exitAndFlushLogs("id");
+ }
+
+ private TestIdentifier testIdentifier() {
+ TestDescriptor descriptor = mock(TestDescriptor.class);
+ UniqueId id = mock(UniqueId.class);
+ when(id.toString()).thenReturn("id");
+ when(descriptor.getUniqueId()).thenReturn(id);
+
+ when(descriptor.getParent()).thenReturn(Optional.empty());
+ when(descriptor.getType()).thenReturn(TestDescriptor.Type.CONTAINER);
+
+ return TestIdentifier.from(descriptor);
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerImplTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerImplTest.java
new file mode 100644
index 0000000..a2f6d55
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerImplTest.java
@@ -0,0 +1,134 @@
+package com.github.zeldigas.rtlogs;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class LoggingControllerImplTest {
+
+ private TrackingAppender appender;
+ private LoggingControllerImpl controller = new LoggingControllerImpl();
+
+ @BeforeEach
+ void before() throws Exception {
+ LoggerContext loggerContext = getLoggerContext();
+ loggerContext.reset();
+ JoranConfigurator configurator = new JoranConfigurator();
+ try(InputStream is = LoggingControllerImplTest.class.getResourceAsStream("/sample-config.xml")) {
+ configurator.setContext(loggerContext);
+ configurator.doConfigure(is);
+ }
+ appender = (TrackingAppender) getLoggerContext().getLogger("ROOT").getAppender("TEST");
+ }
+
+ private LoggerContext getLoggerContext() {
+ return (LoggerContext) LoggerFactory.getILoggerFactory();
+ }
+
+ @Test
+ void capturingDoesNotStartStopAppenders() {
+ controller.startCapture();
+
+ assertNull(getLoggerContext().getLogger("ROOT").getAppender("TEST"));
+
+ LoggerFactory.getLogger("test").warn("hello");
+
+ controller.stopCapture();
+
+ assertSame(appender, getLoggerContext().getLogger("ROOT").getAppender("TEST"));
+
+ assertEquals(1, appender.getStartCount());
+ assertEquals(0, appender.getStopCount());
+ }
+
+ @Test
+ void noEventsForCleanExits() {
+ controller.startCapture();
+
+ Logger logger = LoggerFactory.getLogger("test");
+
+ controller.enter("first");
+
+ logger.warn("message");
+
+ controller.enter("second");
+
+ logger.warn("message1");
+
+ controller.exit("second");
+ controller.exit("first");
+ controller.stopCapture();
+
+ assertEquals(0, appender.getEvents().size());
+ }
+
+ @Test
+ void eventsFlushOnExit() {
+ controller.startCapture();
+
+ Logger logger = LoggerFactory.getLogger("test");
+
+ controller.enter("first");
+
+ logger.warn("message");
+
+ controller.enter("second");
+
+ logger.warn("ignored");
+
+ controller.exit("second");
+
+ logger.error("error1");
+
+ controller.exitAndFlushLogs("first");
+
+ controller.stopCapture();
+
+ assertEquals(2, appender.getEvents().size());
+ assertEquals(Arrays.asList("WARN:message", "ERROR:error1"), appender.getEvents().stream().map(e ->
+ e.getLevel().toString() + ":"+e.getMessage()).collect(Collectors.toList()));
+ }
+
+ @Test
+ void eventsFlushOnInvalidExitOrder() {
+ controller.startCapture();
+
+ Logger logger = LoggerFactory.getLogger("test");
+
+ controller.enter("first");
+
+ logger.warn("message");
+
+ controller.enter("second");
+
+ logger.warn("message1");
+
+ controller.exit("first");
+
+
+ logger.error("not flushed first as stack was cleared");
+ controller.exit("second");
+
+ assertEquals(2, appender.getEvents().size());
+ assertEquals(Arrays.asList("WARN:message", "WARN:message1"), appender.getEvents().stream().map(e ->
+ e.getLevel().toString() + ":"+e.getMessage()).collect(Collectors.toList()));
+
+ controller.exitAndFlushLogs("anything");
+
+ assertEquals(3, appender.getEvents().size());
+ assertEquals(Arrays.asList("WARN:message", "WARN:message1", "ERROR:not flushed first as stack was cleared"), appender.getEvents().stream().map(e ->
+ e.getLevel().toString() + ":"+e.getMessage()).collect(Collectors.toList()));
+
+ controller.stopCapture();
+
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerTest.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerTest.java
new file mode 100644
index 0000000..457dc7f
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/LoggingControllerTest.java
@@ -0,0 +1,62 @@
+package com.github.zeldigas.rtlogs;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.AbstractMap;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class LoggingControllerTest {
+
+ private List varsToPreserve = Arrays.asList("rtlogs.mode", "rtlogs.debug", "sun.java.command");
+ private Map preservedProperties;
+
+ @BeforeEach
+ void setUp() {
+ preservedProperties = varsToPreserve.stream()
+ .map(name -> new AbstractMap.SimpleEntry(name, System.getProperty(name)) {})
+ .filter(i -> i.getValue() != null)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ @AfterEach
+ void tearDown() {
+ preservedProperties.forEach(System::setProperty);
+ }
+
+ @Test
+ void nopController() {
+ System.setProperty("rtlogs.mode", "nop");
+
+ assertTrue(LoggingController.getInstance() instanceof NopLoggingController);
+ }
+
+ @Test
+ void reasonableController() {
+ System.setProperty("rtlogs.mode", "reasonable");
+
+ assertTrue(LoggingController.getInstance() instanceof LoggingControllerImpl);
+ }
+
+ @Test
+ void autoResultingToNop() {
+ System.setProperty("rtlogs.mode", "auto");
+ System.setProperty("sun.java.command", "com.intellij.rt.junit");
+
+ assertTrue(LoggingController.getInstance() instanceof NopLoggingController);
+ }
+
+ @Test
+ void autoResultingToReasonable() {
+ System.setProperty("rtlogs.mode", "auto");
+ System.setProperty("sun.java.command", "something else");
+
+ assertTrue(LoggingController.getInstance() instanceof LoggingControllerImpl);
+ }
+}
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/TrackingAppender.java b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/TrackingAppender.java
new file mode 100644
index 0000000..e27e59d
--- /dev/null
+++ b/reasonable-test-logs/src/test/java/com/github/zeldigas/rtlogs/TrackingAppender.java
@@ -0,0 +1,43 @@
+package com.github.zeldigas.rtlogs;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TrackingAppender extends AppenderBase {
+
+ private int startCount;
+ private int stopCount;
+ private List events = new ArrayList<>();
+
+ @Override
+ protected void append(ILoggingEvent eventObject) {
+ events.add(eventObject);
+ }
+
+ @Override
+ public void start() {
+ super.start();
+ startCount++;
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ stopCount++;
+ }
+
+ public int getStartCount() {
+ return startCount;
+ }
+
+ public int getStopCount() {
+ return stopCount;
+ }
+
+ public List getEvents() {
+ return events;
+ }
+}
diff --git a/reasonable-test-logs/src/test/resources/bad_file.txt b/reasonable-test-logs/src/test/resources/bad_file.txt
new file mode 100644
index 0000000..77e649c
--- /dev/null
+++ b/reasonable-test-logs/src/test/resources/bad_file.txt
@@ -0,0 +1 @@
+a=\u1
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/resources/reasonable-test-logs.properties b/reasonable-test-logs/src/test/resources/reasonable-test-logs.properties
new file mode 100644
index 0000000..62f97c5
--- /dev/null
+++ b/reasonable-test-logs/src/test/resources/reasonable-test-logs.properties
@@ -0,0 +1,2 @@
+rtlogs.mode=nop
+rtlogs.debug=false
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/resources/rt-reasonable.properties b/reasonable-test-logs/src/test/resources/rt-reasonable.properties
new file mode 100644
index 0000000..2da9e62
--- /dev/null
+++ b/reasonable-test-logs/src/test/resources/rt-reasonable.properties
@@ -0,0 +1,2 @@
+rtlogs.mode=reasonable
+rtlogs.debug=true
\ No newline at end of file
diff --git a/reasonable-test-logs/src/test/resources/sample-config.xml b/reasonable-test-logs/src/test/resources/sample-config.xml
new file mode 100644
index 0000000..4b43047
--- /dev/null
+++ b/reasonable-test-logs/src/test/resources/sample-config.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file