Skip to content

Commit

Permalink
Improve test coverage, unpack component msgs on throwables
Browse files Browse the repository at this point in the history
  • Loading branch information
zml2008 committed May 22, 2022
1 parent a67d3dd commit c962f4b
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 43 deletions.
5 changes: 1 addition & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ configurate-v4 = "org.spongepowered:configurate-core:4.1.2"
# text-logger-slf4j
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
slf4j-ext = { module = "org.slf4j:slf4j-ext", version.ref = "slf4j" }
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
mockSlf4j = "org.simplify4u:slf4j-mock:2.2.0"
mockito = { module = "org.mockito:mockito-core", version.ref = "mockito" }
mockito-junit = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
slf4jtest = "com.github.valfirst:slf4j-test:2.6.1" # Specific versions are needed for different SLF4J versions

# text-serializer-gson
gson = "com.google.code.gson:gson:2.8.0"
Expand Down
5 changes: 1 addition & 4 deletions text-logger-slf4j/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ dependencies {
api(projects.adventureApi)
api(libs.slf4j)
implementation(libs.slf4j.ext)
testImplementation(libs.slf4j.simple)
testImplementation(libs.mockSlf4j)
testImplementation(libs.mockito)
testImplementation(libs.mockito.junit)
testImplementation(libs.slf4jtest)
}

sourceSets.main {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* This file is part of adventure, licensed under the MIT License.
*
* Copyright (c) 2017-2022 KyoriPowered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.kyori.adventure.text.logger.slf4j;

import java.util.function.Function;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.util.ComponentMessageThrowable;
import org.jetbrains.annotations.Nullable;

/**
* A wrapper for exceptions that implement ComponentMessageThrowable.
*/
final class UnpackedComponentThrowable extends Throwable {
private static final long serialVersionUID = -1L;

private final Class<? extends Throwable> backingType;

static Throwable unpack(final Throwable maybeRich, final Function<Component, String> serializer) {
if (!(maybeRich instanceof ComponentMessageThrowable)) return maybeRich; // TODO: do we need to unwrap any nested exceptions?

final @Nullable Component message = ((ComponentMessageThrowable) maybeRich).componentMessage();
final Throwable cause = maybeRich.getCause() != null ? unpack(maybeRich.getCause(), serializer) : null;
final Throwable[] suppressed = maybeRich.getSuppressed();

final UnpackedComponentThrowable ret = new UnpackedComponentThrowable(maybeRich.getClass(), serializer.apply(message), cause);
ret.setStackTrace(maybeRich.getStackTrace());
if (suppressed.length > 0) {
for (int i = 0; i < suppressed.length; i++) {
ret.addSuppressed(unpack(suppressed[i], serializer));
}
}

return ret;
}

private UnpackedComponentThrowable(final Class<? extends Throwable> backingType, final String serializedMessage, final Throwable cause) {
super(serializedMessage, cause);
this.backingType = backingType;
}

@Override
public String toString() {
final String className = this.backingType.getName();
final String message = this.getMessage();
return message == null ? className : className + ":" + message;
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.util.function.Function;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
Expand All @@ -34,8 +35,6 @@
final class WrappingComponentLoggerImpl extends LoggerWrapper implements ComponentLogger {
private static final String FQCN = WrappingComponentLoggerImpl.class.getName();

// TODO: maybe handle ComponentMessageThrowable logging somehow?

private final Function<Component, String> serializer;

WrappingComponentLoggerImpl(final Logger backing, final Function<Component, String> serializer) {
Expand All @@ -48,20 +47,24 @@ private String serialize(final Component input) {
}

private Object maybeSerialize(final @Nullable Object input) {
if (input instanceof Component) {
return this.serialize((Component) input);
if (input instanceof ComponentLike) {
return this.serialize(((ComponentLike) input).asComponent());
} else {
return input;
}
}

private Object[] maybeSerialize(final @Nullable Object@NotNull... args) {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Component) {
args[i] = this.serialize((Component) args[i]);
if (args[i] instanceof ComponentLike) {
args[i] = this.serialize(((ComponentLike) args[i]).asComponent());
}
}

if (args.length > 0 && args[args.length - 1] instanceof Throwable) {
args[args.length - 1] = UnpackedComponentThrowable.unpack((Throwable) args[args.length - 1], this.serializer);
}

return args;
}

Expand Down Expand Up @@ -97,7 +100,7 @@ public void trace(final @NotNull Component format, final @Nullable Object @NotNu
public void trace(final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isTraceEnabled()) return;

this.trace(this.serialize(msg), t);
this.trace(this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -132,7 +135,7 @@ public void trace(final Marker marker, final @NotNull Component format, final @N
public void trace(final Marker marker, final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isTraceEnabled(marker)) return;

this.trace(marker, this.serialize(msg), t);
this.trace(marker, this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -167,7 +170,7 @@ public void debug(final @NotNull Component format, final @Nullable Object @NotNu
public void debug(final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isDebugEnabled()) return;

this.debug(this.serialize(msg), t);
this.debug(this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -202,7 +205,7 @@ public void debug(final Marker marker, final @NotNull Component format, final @N
public void debug(final Marker marker, final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isDebugEnabled(marker)) return;

this.debug(marker, this.serialize(msg), t);
this.debug(marker, this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -237,7 +240,7 @@ public void info(final @NotNull Component format, final @Nullable Object @NotNul
public void info(final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isInfoEnabled()) return;

this.info(this.serialize(msg), t);
this.info(this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -272,7 +275,7 @@ public void info(final Marker marker, final @NotNull Component format, final @Nu
public void info(final Marker marker, final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isInfoEnabled(marker)) return;

this.info(marker, this.serialize(msg), t);
this.info(marker, this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -307,7 +310,7 @@ public void warn(final @NotNull Component format, final @Nullable Object @NotNul
public void warn(final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isWarnEnabled()) return;

this.warn(this.serialize(msg), t);
this.warn(this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -342,7 +345,7 @@ public void warn(final Marker marker, final @NotNull Component format, final @Nu
public void warn(final Marker marker, final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isWarnEnabled(marker)) return;

this.warn(marker, this.serialize(msg), t);
this.warn(marker, this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -377,7 +380,7 @@ public void error(final @NotNull Component format, final @Nullable Object @NotNu
public void error(final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isErrorEnabled()) return;

this.error(this.serialize(msg), t);
this.error(this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}

@Override
Expand Down Expand Up @@ -412,6 +415,6 @@ public void error(final Marker marker, final @NotNull Component format, final @N
public void error(final Marker marker, final @NotNull Component msg, final @Nullable Throwable t) {
if (!this.isErrorEnabled(marker)) return;

this.error(marker, this.serialize(msg), t);
this.error(marker, this.serialize(msg), UnpackedComponentThrowable.unpack(t, this.serializer));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,128 @@
*/
package net.kyori.adventure.text.logger.slf4j;

import com.github.valfirst.slf4jtest.LoggingEvent;
import com.github.valfirst.slf4jtest.TestLogger;
import com.github.valfirst.slf4jtest.TestLoggerFactory;
import com.github.valfirst.slf4jtest.TestLoggerFactoryExtension;
import com.google.common.collect.ImmutableList;
import java.util.List;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.junit.jupiter.api.BeforeEach;
import net.kyori.adventure.util.ComponentMessageThrowable;
import org.jetbrains.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

@ExtendWith(MockitoExtension.class)
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(TestLoggerFactoryExtension.class)
public class ComponentLoggerTest {
@Mock(name = "net.kyori.adventure.test.ComponentLoggerTest")
Logger backingLogger;
private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ComponentLoggerTest.class);

private final ComponentLoggerProvider.LoggerHelper helper = new Handler.LoggerHelperImpl();
private static final Marker MARKED = MarkerFactory.getMarker("MARKED");

ComponentLogger makeLogger() {
return new WrappingComponentLoggerImpl(this.backingLogger, this.helper.plainSerializer());
return ComponentLogger.logger();
}

@BeforeEach
void enableLevels() {
Mockito.when(this.backingLogger.isInfoEnabled()).thenReturn(true);
@Test
void testCallerLogger() {
final ComponentLogger logger = ComponentLogger.logger();
assertEquals(this.getClass().getName(), logger.getName());
}

@Test
void testLogSimple() {
final Component toLog = Component.text().content("Hello ").color(NamedTextColor.RED).append(Component.translatable("location.world")).build();
final Component toLog = Component.text()
.content("Hello ")
.color(NamedTextColor.RED)
.append(Component.translatable("location.world"))
.build();

this.makeLogger().info(toLog);

assertEquals(LOGGER.getLoggingEvents(), ImmutableList.of(LoggingEvent.info("Hello location.world")));
}

@Test
void testComponentArg() {
final Component message = Component.text("Hello ").append(Component.text("{}", NamedTextColor.BLUE));
final Component arg = Component.selector("@s");

final ComponentLogger logger = this.makeLogger();
this.makeLogger().warn(message, arg);

logger.info(toLog);
assertEquals(LOGGER.getLoggingEvents(), ImmutableList.of(LoggingEvent.warn("Hello {}", "@s")));
}

@Test
void testStringArg() {
final Component message = Component.text("Hello ").append(Component.text("{}", NamedTextColor.BLUE));
final String arg = "world";

this.makeLogger().debug(message, arg);

assertEquals(LOGGER.getLoggingEvents(), ImmutableList.of(LoggingEvent.debug("Hello {}", arg)));
}

@Test
void testMultiArgs() {
final Component message = Component.text("Good morning! The time is {} and you have {} cats!");
final Component arg0 = Component.text("14:28", NamedTextColor.BLUE);
final String arg1 = "11";

Mockito.verify(this.backingLogger, Mockito.times(2)).isInfoEnabled();
Mockito.verify(this.backingLogger).info("Hello location.world");
Mockito.verifyNoMoreInteractions(this.backingLogger);
this.makeLogger().error(message, arg0, arg1);
assertEquals(
LOGGER.getLoggingEvents(),
ImmutableList.of(LoggingEvent.error("Good morning! The time is {} and you have {} cats!", "14:28", "11"))
);
}

@Test
void testUnwrapThrowable() {
final Component message = Component.text("Hello world");
final Exception error = new RichTestException(Component.translatable("test.failed", NamedTextColor.DARK_PURPLE));

this.makeLogger().warn(message, error);

final List<LoggingEvent> events = LOGGER.getLoggingEvents();
assertEquals(1, events.size());
final Throwable thrownException = events.get(0).getThrowable().orElse(null);
assertNotNull(thrownException);

assertEquals("test.failed", thrownException.getMessage());
assertArrayEquals(error.getStackTrace(), thrownException.getStackTrace());
assertTrue(thrownException.toString().startsWith("net.kyori.adventure.text.logger.slf4j.ComponentLoggerTest$RichTestException"));
}

static class RichTestException extends Exception implements ComponentMessageThrowable {
private static final long serialVersionUID = -1l;

private final Component richMessage;

RichTestException(final Component richMessage) {
super("no");
this.richMessage = richMessage;
}

@Override
public @Nullable Component componentMessage() {
return this.richMessage;
}
}

@Test
void testWithMarker() {
final Component message = Component.text("meow :3");
this.makeLogger().info(MARKED, message);
assertEquals(
LOGGER.getLoggingEvents(),
ImmutableList.of(LoggingEvent.info(MARKED, "meow :3"))
);
}
}

0 comments on commit c962f4b

Please sign in to comment.